2017-08-09 116 views
0

我有一個通過反序列化實例化的通信管理器。 現在我想創建一些靜態方法來訪問實例數據。通過本地靜態實例訪問實例成員

在其他帖子上,我看到人們建議不要訪問實例對象的靜態字段。但是現在我創建了下面的代碼,並且像預期的那樣工作,我可以在沒有CommManager的新實例的情況下反序列化並使用靜態方法。大!

問題: 這是安全嗎? 我想申請線程,GetChannel是我的應用程序的核心,並將被多個線程上的許多應用程序部分使用。我認爲重要的是我不會造成表現懲罰或其他後果。請指教。

我問這個原因我覺得很奇怪,我找不到這個方法的任何類似的例子,我現在看到它的方式,我可以很容易地使每個方法都是靜態的,沒有缺點。

public class CommManager 
{ 
    public ObservableCollection<ChannelConfig> channelConfigs;   
    private List<iChannel> channels;   
    private static CommManager StaticMe; 

    public CommManager() 
    { 
     channelConfigs.CollectionChanged += ChannelCollectionChanged; 
     StaticMe = this;    
    } 

    private void ChannelCollectionChanged(object sender, 
     NotifyCollectionChangedEventArgs args) 
    { 
     if (channels == null) 
      channels = new List<iChannel>(); 
     switch (args.Action) 
     { 
      case NotifyCollectionChangedAction.Add: 
       foreach (ChannelConfig newItem in args.NewItems) 
        channels.Add(CreateChannel(newItem)); 
       break; 
      case Notif.. /// etc. etc. 
     } 
    } 

    /// <summary> 
    /// I can access this method without instance and i get normal de-serialized values 
    /// </summary> 
    public static iChannel GetChannel(CommChannel channelnr) 
    { 
     return StaticMe.Channels[(int)channelnr]; 
    } 
} 

回答

0

當您創建班級的第二個實例時,可能會出現問題。

靜態字段只能存在一次。當你覆蓋一個靜態字段時,它會改變對新對象的引用。

任何舊的參考設置舊對象,可能仍然會在別處使用將而不是更新 - 或更糟糕的是,如果它包含任何數據,它可能會意外更改。

CommManager manager = new CommManager(); 
manager.GetChannel(); 
// refers to the static field StaticMe.channels which is manager.channels. 

CommManager manager2 = new CommManager(); 
manager2.GetChannel(); 
// refers to the static field StaticMe.channels which is manager2.channels. 
manager.GetChannel(); 
// still exists, now refers to manager2.channels because of StaticMe now being manager2 
// It is not retaining any of the original channels because of the changed reference. 

正因爲如此,靜態字段或屬性最好是不可改變的,而不是依賴於存儲比默認或固定值以外的靜態屬性中的任何東西。

如果您確實必須全局存儲可變值,請不要在構造函數或任何實例方法中將它們分配,而應將其分配給定義。

如果您的頻道應該在全球範圍內可用,那麼通過將該靜態只讀(因爲列表引用將保持不變,只有內容可能會更改)使全局可用。

// for brevity, removed all unchanged parts... 
public class CommManager 
{ 
    private static readonly List<IChannel> channels = new List<IChannel>(); 

    private void ChannelCollectionChanged(object sender, 
     NotifyCollectionChangedEventArgs args) 
    { 
     switch (args.Action) 
     { 
      case NotifyCollectionChangedAction.Add: 
       foreach (ChannelConfig newItem in args.NewItems) 
        CommManager.channels.Add(CreateChannel(newItem)); 
       break; 
      case Notif.. /// etc. etc. 
     } 
    } 

    public static IChannel GetChannel(CommChannel channelNr) 
    { 
     return CommManager.channels[(int)channelNr]; 
    } 
} 

此外,如果你要一個多線程的方式,你可能需要一個ConcurrentBag<T>是線程安全的,而不是一個正常List<T>這是不。

+0

謝謝,你的回答很明確,就像一個魅力! – robbi

+0

在代碼後面我試圖重新分配頻道= channels.OrderBy(blabla).ToList()我現在清楚地看到,我不能重新分配這個,並理解你的方法的好處。謝謝 – robbi

+0

@robbi如果你想要它,你可以使用'OrderedList ',它可以非常有效地爲你排序,或者 - 如編輯中所寫 - 如果你想使用多線程並且需要線程安全的集合,請看看['Concurrent ... 'collections](https://msdn.microsoft.com/en-US/library/system.collections.concurrent(v = vs.110).aspx),無論如何它們都實現了線程安全。 – Adwaenyth