属性从不为空c#



重构代码时,我会遇到如下这样的实例

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

稍后在一个方法中,我看到以下内容:

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

假设_property只由Property的设置器设置,那么这个代码是多余的吗?

有没有办法,通过反射魔法或其他方法,_property可以为空?

假设_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对象应该有三个属性:IdFirstNameLastName

当有人读到这个类时,一个直接的问题是,为什么有人要使用这里的属性?

答案也是一样的,它们提供了一种封装机制。但让我们考虑一下,从长远来看,这对我们有什么帮助。假设有一天,有人决定客户的名字应该是一个长度小于20的字符串。如果上述类别已声明如下:

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

那么我们应该在我们创建的每个实例中检查FirstName的长度!否则,如果我们选择了带有属性的声明,我们就可以很容易地使用数据注释

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; }
}

考虑以上两种方法,必须重新访问所有的代码库并进行检查。很明显,房地产是赢家。

是的,通过反射是可能的。尽管如此,我并不担心反思——我不担心有人用反思来破坏你课的设计。

然而,我确实担心的是:"假设_property只由property的setter设置"这句话是关键。您正在阻止类的用户将属性设置为null。

但是,您不能阻止您自己或类的其他维护者忘记只使用类内部的属性。事实上,您的示例中有人从类内部检查字段,而不是属性本身。。。。这意味着,在类中,访问来自字段和属性。

在大多数情况下(问题只能来自类内部),我会使用断言并断言字段不是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;
}

它基本上是多余的。然而,如果它是关键任务,或者由于某种原因造成了可怕的副作用,它可能会继续存在。这很难说,但你的部分问题是"反射能把这个值改为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; }
    }
}

我知道这个问题很老了,但我需要我的一个字符串属性永远不会出现null。

所以我做了这个,它对我起到了作用

public string Operation { get; set; } = string.Empty;

通过这种方式,默认值是一个空字符串,但从不为null。

相关内容

  • 没有找到相关文章

最新更新