C#中的"常量正确性"

时间:2020-03-06 14:31:59  来源:igfitidea点击:

const正确性的重点是能够提供用户无法更改或者删除的实例的视图。编译器通过指出何时从const函数中破坏constness或者尝试使用const对象的非const函数来支持此功能。因此,无需复制const方法,是否可以在C中使用具有相同目的的方法?

我知道不变性,但这并没有真正延续到容器对象上,仅举一个例子。

解决方案

C没有这种功能。我们可以按值或者引用传递参数。引用本身是不可变的,除非我们指定ref修饰符。但是引用的数据并不是一成不变的。因此,如果要避免副作用,则需要小心。

MSDN:

传递参数

为了获得const-craziness(或者函数式编程术语的纯净)的好处,我们将需要以某种方式设计类,使其不可变,就像cis的String类一样。

这种方法比仅将对象标记为只读要好得多,因为使用不可变的类,我们可以在多任务环境中轻松传递数据。

  • const关键字可用于编译时间常数,例如原始类型和字符串
  • readonly关键字可用于运行时常量,例如引用类型

只读的问题在于,它仅允许引用(指针)为常数。引用(指向)的内容仍可以修改。这是棘手的部分,但是没有办法解决。实现常量对象意味着使它们不公开任何可变的方法或者属性,但这很尴尬。

另请参见有效的C#:改进C的50种特定方法(第2项建议以const方式为只读)。

我也多次遇到这个问题,最终使用接口。

我认为重要的是,放弃任何形式的Cis,甚至C ++的演变。它们是两种共享几乎相同语法的不同语言。

我通常在C中通过定义类的只读视图来表达"常量正确性":

public interface IReadOnlyCustomer
{
    String Name { get; }
    int Age { get; }
}

public class Customer : IReadOnlyCustomer
{
    private string m_name;
    private int m_age;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        set { m_age = value; }
    }
}

接口是答案,并且实际上比C ++中的" const"更强大。 const是一种"一刀切"的解决方案,其中的问题是将" const"定义为"不设置成员或者调用某些设置成员的东西"。在许多情况下,这是保持一致性的好捷径,但并非所有情况都如此。例如,考虑一个函数,该函数根据某些成员计算值,但也缓存结果。在C ++中,这被认为是非const,尽管从用户的角度来看,它本质上是const。

接口使我们可以更灵活地定义要从类中提供的功能的特定子集。想要保持不变?只需提供没有任何变异方法的接口即可。是否要允许设置某些东西,而不允许其他东西?仅提供带有这些方法的接口。

我只是想为我们指出,许多System.Collections.Generics容器都有一个AsReadOnly方法,它将为我们提供不可变的集合。

与其他一些人达成一致的看法是,使用在构造函数中初始化的只读字段创建不可变对象。

public class Customer
    {
    private readonly string m_name;
    private readonly int m_age;

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
    }

    public int Age
    {
        get { return m_age; }
    }
  }

或者,我们也可以在属性上添加访问范围,即public get和protected set?

public class Customer
    {
    private string m_name;
    private int m_age;

    protected Customer() 
    {}

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
        protected set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        protected set { m_age = value; }
    }
  }