如何拥有C#只读功能,但不限于构造函数?

如何拥有C#只读功能,但不限于构造函数?,c#,readonly,lazy-initialization,C#,Readonly,Lazy Initialization,C#“readonly”关键字是一个修饰符,当字段声明包含它时,该声明引入的字段赋值只能作为声明的一部分或在同一类的构造函数中发生 现在假设我确实想要这个“赋值一次”约束,但我更愿意允许赋值在构造函数之外进行,可能是延迟/延迟的计算/初始化 我怎么能这么做?有没有可能用一种很好的方式,比如说,有没有可能写一些属性来描述它 现在假设我确实想要这个“赋值一次”约束,但我宁愿允许赋值在构造函数之外完成 请注意,延迟初始化非常复杂,因此对于所有这些答案,如果有多个线程试图访问对象,则应小心 如果你想在课

C#“readonly”关键字是一个修饰符,当字段声明包含它时,该声明引入的字段赋值只能作为声明的一部分或在同一类的构造函数中发生

现在假设我确实想要这个“赋值一次”约束,但我更愿意允许赋值在构造函数之外进行,可能是延迟/延迟的计算/初始化

我怎么能这么做?有没有可能用一种很好的方式,比如说,有没有可能写一些属性来描述它

现在假设我确实想要这个“赋值一次”约束,但我宁愿允许赋值在构造函数之外完成

请注意,延迟初始化非常复杂,因此对于所有这些答案,如果有多个线程试图访问对象,则应小心

如果你想在课堂上这样做

您可以使用C#4.0内置的惰性初始化功能:

或者对于较旧版本的C#,只需提供一个
get
方法,并检查是否已使用支持字段初始化:

public string SomeValue
{
    get
    {
        // Note: Not thread safe...
        if(someValue == null)
        {
            someValue = InitializeSomeValue(); // Todo: Implement
        }

        return someValue;
    }
}
如果你想在课外进行此操作

你想要冰棒的不变性:

  • (搜索“冰棒不变性”,你会找到一个视频)
基本上:

  • 您使整个类可写,但添加了
    Freeze
    方法
  • 调用此冻结方法后,如果用户试图调用类上的setter或mutator方法,则抛出
    ModifyFrozenObjectException
  • 您可能需要一种外部类确定类
    是否冻结的方法
顺便说一句,这些名字是我刚刚编的。诚然,我的选择很差,但这方面还没有普遍遵循的惯例

现在,我建议您创建一个
IFreezable
接口,以及可能的相关异常,这样您就不必依赖于WPF实现。比如:

public interface IFreezable
{
    void Freeze();
    bool IsFrozen { get; }
}
您可以使用该类:

private readonly Lazy\u foo=new Lazy(GetFoo);
公共福福
{
获取{return\u foo.Value;}
}
私有静态Foo GetFoo()
{
//以某种方式创建一个Foo。。。
}

GetFoo
仅在您第一次调用Foo属性时才会被调用。

如果我正确理解您的问题,听起来您只想设置一次字段的值(第一次),而不允许在此之后设置它。如果是这样的话,那么之前所有关于使用Lazy(和相关的)的帖子都可能有用。但如果你不想使用这些建议,也许你可以这样做:

public class SetOnce<T> 
{
    private T mySetOnceField;
    private bool isSet;

    // used to determine if the value for 
    // this SetOnce object has already been set.
    public bool IsSet
    {
      get { return isSet; }
    }
    // return true if this is the initial set, 
    // return false if this is after the initial set.
    // alternatively, you could make it be a void method
    // which would throw an exception upon any invocation after the first.
    public bool SetValue(T value)
    {
       // or you can make thread-safe with a lock..
       if (IsSet)
       {
          return false; // or throw exception.
       }
       else 
       {
          mySetOnceField = value;
          return isSet = true;
       }
    }

    public T GetValue()
    {
      // returns default value of T if not set. 
      // Or, check if not IsSet, throw exception.
      return mySetOnceField;         
    }
} // end SetOnce

public class MyClass 
{
  private SetOnce<int> myReadonlyField = new SetOnce<int>();
  public void DoSomething(int number)
  {
     // say this is where u want to FIRST set ur 'field'...
     // u could check if it's been set before by it's return value (or catching the exception).
     if (myReadOnlyField.SetValue(number))
     {
         // we just now initialized it for the first time...
         // u could use the value: int myNumber = myReadOnlyField.GetValue();
     }
     else
     {
       // field has already been set before...
     }

  } // end DoSomething

} // end MyClass
public类SetOnce
{
私人T mySetOnceField;
私人住宅;
//用于确定
//已设置此SetOnce对象。
公共图书馆
{
获取{return isSet;}
}
//如果这是初始设置,则返回true,
//如果在初始设置之后返回false。
//或者,您可以将其设置为void方法
//这将在第一次调用之后的任何调用上引发异常。
公共布尔设置值(T值)
{
//或者你可以用锁来保证线程安全。。
如果(IsSet)
{
返回false;//或引发异常。
}
其他的
{
mySetOnceField=值;
返回isSet=true;
}
}
公共T GetValue()
{
//如果未设置,则返回T的默认值。
//或者,检查是否未设置,抛出异常。
返回mySetOnceField;
}
}//结束设置一次
公共类MyClass
{
private SetOnce myReadonlyField=new SetOnce();
公共无效剂量测定(整数)
{
//假设这是您要首先设置“字段”的位置。。。
//你可以通过它的返回值(或者捕捉异常)来检查它之前是否被设置过。
if(myReadOnlyField.SetValue(数字))
{
//我们刚刚第一次初始化了它。。。
//您可以使用以下值:int myNumber=myReadOnlyField.GetValue();
}
其他的
{
//之前已设置字段。。。
}
}//结束剂量测定
}//结束MyClass
这在埃菲尔铁塔中被称为“一次”功能。这是C#的一个重大疏忽。新的Lazy类型是一个糟糕的替代品,因为它不能与其非Lazy版本互换,而是要求您通过其value属性访问包含的值。因此,我很少使用它。噪声是C代码最大的问题之一。理想情况下,一个人想要这样的东西

public once Type PropertyName { get { /* generate and return value */ } }
与当前的最佳实践相反

Type _PropertyName; //where type is a class or nullable structure
public Type PropertyName
{
    get
    {
        if (_PropertyName == null)
            _PropertyName = /* generate and return value */ 
        return _PropertyName
    }
}

LazyInitializer
类,也许。还有
懒惰。请参阅。签出:+1;这也是一个不错的选择。有很多不同的方法来分割它:)OP评论中的线程也显示了一个类似于Kevin的解决方案-
Type _PropertyName; //where type is a class or nullable structure
public Type PropertyName
{
    get
    {
        if (_PropertyName == null)
            _PropertyName = /* generate and return value */ 
        return _PropertyName
    }
}