在重构代码时,我想出了以下
之类的实例private string _property = string.Empty;
public string Property
{
set { _property = value ?? string.Empty); }
}
稍后在方法中我会看到以下内容:
if (_property != null)
{
//...
}
假设_property
仅由Property
的设置者设置,此代码是多余的吗?
有没有办法,通过反射魔法或_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; }
}
我们已经宣布了一个代表客户的类。 Customer对象应具有三个属性Id
,FirstName
和LastName
。
一个直接的问题,当有人读这个类时,为什么有人会在这里使用属性?
答案再次相同,它们提供了封装机制。但是,让我们考虑一下从长远来看这对我们有什么帮助。让我们说有一天有人决定客户的名字应该是一个长度小于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 :(得分:2)
是的,可以通过反思来实现。尽管如此,我不会担心反思 - 使用反射来打败你班级设计的人不是我担心的。
然而,有一些我担心的事情:短语“假设_property仅由属性的setter设置”是关键。您正在阻止类的用户将property设置为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;
}
答案 2 :(得分:1)
基本上是多余的。但是,如果它是关键任务或由于某种原因它造成了可怕的副作用,它可能会保留。很难说,但你的问题的一部分是&#34;反射会将此值更改为null&#34;答案是肯定的,可以在这个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; }
}
}
答案 3 :(得分:0)
我知道这个问题很旧,但是,我需要我的字符串属性之一永远不会出现为空。
所以我做到了,它为我工作了
public string Operation { get; set; } = string.Empty;
通过这种方式,默认值是一个空字符串,但绝不能为空。