2015-11-19 95 views
3

當重構代碼,我拿出實例像下面屬性不能爲null C#

private string _property = string.Empty; 
public string Property 
{ 
    set { _property = value ?? string.Empty); } 
} 

後來在一個方法我看到以下內容:

if (_property != null) 
{ 
    //... 
} 

假設_property只被設定這個代碼是Property,這個代碼是多餘的嗎?

I.e是否有任何方法,通過反射魔術或其他方法_property可以爲空?

+0

宇宙射線和硬件錯誤總是會導致該值爲空。當我看到像這樣的東西時,我把它歸因於「小心謹慎」。內部字段可能有很多種方式(例如輸入的序列化),但是大多數方法都是非常特殊的,並且應該從上下文中清楚。 –

回答

4

假設_property只由Property的setter設置,這是否是 代碼冗餘?

確切地說,它是多餘的。這是Properties的實際目的。我們不應該直接訪問類的字段。我們應該使用Property來訪問它們。所以在相應的setter中,我們可以嵌入任何邏輯,我們可以保證每次我們嘗試設置一個值時,這個邏輯都會再次被驗證。這個論證甚至適用於一個類的方法。在一個方法中,我們必須使用屬性而不是實際的字段。而且,當我們想要讀取一個字段的值時,我們應該使用相應的getter。

通常,屬性增強了封裝的概念,封裝是面向對象編程OOP的支柱之一。

很多時候,當我們想要設置一個值時,沒有任何邏輯應該應用。舉例如下例:

public class Customer 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

我們已經聲明瞭一個代表客戶的類。客戶對象應具有三個屬性IdFirstNameLastName

一個直接的問題,當有人讀這個類時,爲什麼有人應該使用這裏的屬性?

答案也是一樣的,它們提供了封裝機制。但是讓我們考慮一下,從長遠來看,這對我們有什麼幫助。比方說,有一天,有人決定一個客戶的名字應該是長度小於20的字符串,如果上面的類被聲明如下:

public class Customer 
{ 
    public int Id; 
    public string FirstName; 
    public string LastName; 
} 

那麼我們就應該檢查的FirstName長度在我們創建的每個實例中!否則,如果我們有所回升與性能的聲明,我們可以很容易地只利用Data Annotations

public class Customer 
{ 
    public int Id { get; set; } 
    [StringLength(20)] 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

,就是這樣。另一種方法可能是以下幾點:

public class Customer 
{ 
    public int Id { get; set; } 
    private string firstName; 
    public string FirstName 
    { 
     get { return firstName } 
     set 
     { 
      if(value!=null && value.length<20) 
      { 
       firstName = value; 
      } 
      else 
      { 
       throw new ArgumentException("The first name must have at maxium 20 characters", "value"); 
      } 
     } 
    } 
    public string LastName { get; set; } 
} 

具有重溫所有你的代碼,使該檢查考慮上述兩種方法的。物業贏得的結果很清楚。

1

它基本上是多餘的。然而,如果它是關鍵任務或者由於某種原因造成可怕的副作用,它可能會保留。這是很難說的,但你的問題的一部分是「可以反射改變這個值設置爲null」這個問題的答案是肯定的,這裏可以看到在這個linqpad演示

void Main() 
{ 
    var test = new Test(); 
    test.Property = "5"; 
    Console.WriteLine(test.Property);//5 

    FieldInfo fieldInfo = test.GetType().GetField("_property",BindingFlags.NonPublic | BindingFlags.Instance); 
    fieldInfo.SetValue(test, null); 
    Console.WriteLine(test.Property);//null 
} 

public class Test 
{ 
    private string _property = string.Empty; 
    public string Property 
    { 
     get { return _property; } 
     set { _property = value ?? string.Empty; } 
    } 
} 
2

是的,這是可以通過反射。儘管如此,我並不擔心反思 - 使用反思來挫敗課堂設計的人並不是我所擔心的。

然而,有一些我確實擔心的是:「假設_property只由Property的setter設置」是關鍵。您正在阻止您的課程的用戶將屬性設置爲null。

但是,您不會阻止您的班級的其他維護人員忘記僅使用班級內的屬性。事實上,你的例子有一個從類內部而不是屬性本身檢查字段......這意味着,在你的類中,訪問來自字段和屬性。

在大多數情況下(問題只能來自類內部),我會使用斷言並斷言該字段不爲空。

如果我真的,真的,真的想確保它不是空(不包括反射或人一意孤行,斷事),你可以嘗試這樣的事:

internal class Program 
{ 
    static void Main() 
    { 
     string example = "Spencer the Cat"; 
     UsesNeverNull neverNullUser = new UsesNeverNull(example); 
     Console.WriteLine(neverNullUser.TheString); 
     neverNullUser.TheString = null; 
     Debug.Assert(neverNullUser.TheString != null); 
     Console.WriteLine(neverNullUser.TheString); 
     neverNullUser.TheString = "Maximus the Bird"; 
     Console.WriteLine(neverNullUser.TheString); 
    } 


} 

public class UsesNeverNull 
{ 
    public string TheString 
    { 
     get { return _stringValue.Value; } 
     set { _stringValue.Value = value; } 
    } 

    public UsesNeverNull(string s) 
    { 
     TheString = s; 
    } 

    private readonly NeverNull<string> _stringValue = new NeverNull<string>(string.Empty, str => str ?? string.Empty); 
} 

public class NeverNull<T> where T : class 
{ 
    public NeverNull(T initialValue, Func<T, T> nullProtector) 
    { 
     if (nullProtector == null) 
     { 
      var ex = new ArgumentNullException(nameof(nullProtector)); 
      throw ex; 
     } 
     _value = nullProtector(initialValue); 
     _nullProtector = nullProtector; 
    } 

    public T Value 
    { 
     get { return _nullProtector(_value); } 
     set { _value = _nullProtector(value); } 
    } 
    private T _value; 
    private readonly Func<T, T> _nullProtector; 
}