2015-11-19 66 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; 
} 
相关问题