可以在构造函数外部赋值的只读字段

发布于 2024-10-04 09:48:24 字数 203 浏览 0 评论 0原文

有没有办法在类中拥有一个私有只读字段,可以在类中的任何位置赋值,但只能赋值一次?

也就是说,我正在寻找一种私有只读类型的字段,它只能被赋值一次,但不一定在构造函数内。因此,如果将一个值重新分配给一个字段,那么它会显示编译时错误(我确信这要求太多了)。

如果有任何模式(不是语言特征)可以完成相同的工作,我也确实有兴趣了解这一点。

感谢您的关注。

Is there a way to have a private readonly field in a class that could be assigned a value anywhere in the class, but only once??

That is, I am looking for a private readonly kind of field which could be assigned a value only once, but not necessarily inside the constructor. So that, if a value is re-assigned to a field then it shows compile-time error ( I am sure that is asking for too much).

If there is any pattern (not language feature) that could do the same job, would really be interested in knowing that too.

Thanks for your interest.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

落花随流水 2024-10-11 09:48:24

您不会收到编译时错误,但您可以使用属性来完成您正在寻找的内容。在设置器中,仅当内部布尔/标志为 false 时才设置该值。第一次设置后,将标志设置为 true。如果 setter 再次被另一段代码调用,您可以抛出 InvalidOperationException 让他们知道它已经被初始化

private bool fieldAlreadySet = false;
private string field;

public string Field
{
   get {return field;}
   set
   {
       if (fieldAlreadySet) throw new InvalidOperationException("field already set");
       this.field = value;
       fieldAlreadySet = true;
   }
}

You won't get compile time error, but you can accomplish what you're looking for with a property. In the setter, only set the value if an internal bool/flag is false. Once you set it the first time, set the flag to true. And if the setter is invoked again by another piece of code, you can throw an InvalidOperationException to let them know it has already been initialized

private bool fieldAlreadySet = false;
private string field;

public string Field
{
   get {return field;}
   set
   {
       if (fieldAlreadySet) throw new InvalidOperationException("field already set");
       this.field = value;
       fieldAlreadySet = true;
   }
}
帥小哥 2024-10-11 09:48:24

简短的回答是否定的。唯一可以进行编译器检查的一次性赋值的地方是在构造函数中。您可以创建一个系统,如果多次尝试分配,则会出现运行时错误,但没有 C# 构造可以执行此操作

The short answer is no. The only place you can have a one-time assignment that is compiler checked is in the constructor. You could create a system where you'd get a run-time error if an assignment was attempted more than once but there is no C# construct to do this

无所谓啦 2024-10-11 09:48:24

您可以创建一个通用结构来自动化该过程:

public struct WriteOnce<T>
{
    private T? _value;

    public T Value
    {
        get { return _value; }
        set
        {
            if (_value.HasValue) throw new InvalidOperationException();
            _value = value;
        }
    }
}

编辑
我刚刚意识到上面的内容甚至无法编译。编译器不会让 _value 的类型为 Nullable,因为 Nullable 中的 T 必须是不可为 null 的类型。

这是一个效果更好的实现:

public struct WriteOnce<T>
{
    private T _value;
    private bool _hasValue;

    public bool HasValue { get { return _hasValue; } }

    public T Value
    {
        get { return _value; }
        set
        {
            if (HasValue) throw new InvalidOperationException();
            _value = value;
            _hasValue = true;
        }
    }

    public static implicit operator T(WriteOnce<T> x)
    {
        return x.Value;
    }

    public WriteOnce(T val)
    {
        _value = val;
        _hasValue = true;
    }
}

注意,我说它会工作得更好 - 并不是说​​它会工作得很好。使用它仍然存在一些问题:

首先,如果编译器检测到您尝试使用它而不先为其分配某些内容,它会抱怨。像这样初始化它会解决这个问题:

WriteOnce<int> foo = default(WriteOnce<int>);

其次,即使它在修改时抛出异常,C# 仍然会很乐意让你覆盖它。因此,虽然这确实封装了一些功能;如果它最终会在对象的接口中公开,您仍然应该将其包装在属性中以防止误用。

private WriteOnce<int> _someInt = default(WriteOnce<int>);
public int SomeInt
{ 
    get { return _someInt; }
    set { _someInt.Value = value; }
}

然而,这仍然没有真正解决我在原始代码片段中犯下的最后一个严重错误,即创建一个可变结构。如果你做了很多这样的事情,为了不重复自己,可能值得违反该原则,但这绝对是一个潜在的陷阱,需要仔细注释并排除在外任何公共接口。

不过,如果这只是一次性的,那么其他人建议的直接实现属性的做法就不那么复杂和安全。

You could create a generic struct to automate the process:

public struct WriteOnce<T>
{
    private T? _value;

    public T Value
    {
        get { return _value; }
        set
        {
            if (_value.HasValue) throw new InvalidOperationException();
            _value = value;
        }
    }
}

EDIT
I just realized the above won't even compile. The compiler doesn't let _value's type to be Nullable, because the T in Nullable must be a non-nullable type.

Here's an implementation that will work a bit better:

public struct WriteOnce<T>
{
    private T _value;
    private bool _hasValue;

    public bool HasValue { get { return _hasValue; } }

    public T Value
    {
        get { return _value; }
        set
        {
            if (HasValue) throw new InvalidOperationException();
            _value = value;
            _hasValue = true;
        }
    }

    public static implicit operator T(WriteOnce<T> x)
    {
        return x.Value;
    }

    public WriteOnce(T val)
    {
        _value = val;
        _hasValue = true;
    }
}

Note I said it would work better - not that it would work well. There are still some gotchas on using it:

First, the compiler will complain if it detects that you're trying to use it without assigning something to it first. Initializing it like so will take care of that:

WriteOnce<int> foo = default(WriteOnce<int>);

Second, even if it throws an exception on modification, C# will still happily let you overwrite it. So while this does encapsulate some of the functionality; you should still wrap it in a property to prevent misuse if it's going to end up exposed in an object's interface.

private WriteOnce<int> _someInt = default(WriteOnce<int>);
public int SomeInt
{ 
    get { return _someInt; }
    set { _someInt.Value = value; }
}

However, that still doesn't really get around the last egregious error I committed in the original snippet, which was creating a mutable struct. If you're doing a lot of something like this it might be worth violating that principle for the sake of not repeating yourself, but this is definitely a potential trap that would need to be commented carefully and kept out of any public interfaces.

If it's just a one-off, though, what others have suggested about just directly implementing a property is less complicated and safer.

凝望流年 2024-10-11 09:48:24

您可以使用私有属性来检查它是否已分配,并且仅在未分配时才分配值。

int? _writeOnceField;

private int? WriteOnce
{
   set
   {
      if (!_writeOnceFiled.HasValue)
      {
        writeOnceFiled = value;
      }
      else
      {
        // Throw exception
      }
   }
}

You can use a private property that checks to see if it was assigned to and only assign a value if it hasn't been.

int? _writeOnceField;

private int? WriteOnce
{
   set
   {
      if (!_writeOnceFiled.HasValue)
      {
        writeOnceFiled = value;
      }
      else
      {
        // Throw exception
      }
   }
}
转角预定愛 2024-10-11 09:48:24

没有语言支持,但您可以创建一个 setter,它实现了所需的行为。显然,这不会给您带来编译时错误,但如果没有语言支持,这可能是您能做的最好的事情。

There's no language support for that, but you could make a setter, which implemented the desired behavior. Obviously that would not give you compile time errors, but without the language support that is probably the best you can do.

青春如此纠结 2024-10-11 09:48:24

这看起来是声明赋值或单例的良好候选者。

public class SomeClass {
    private readonly Type t = typeof(SomeClass);
}

或者,就像其他人所说的那样,创建一个仅带有 setter 的内部属性,然后为其分配一个值。

public class SomeClass {
    private Type t;

    internal Type Type {
        set {
            if (t == null) t = value;
        }
    }
}

This looks like a good candidate for declaration assignment or singleton.

public class SomeClass {
    private readonly Type t = typeof(SomeClass);
}

Or otherwise, like the others said, make an internal or so property with only a setter, then assign a value to it.

public class SomeClass {
    private Type t;

    internal Type Type {
        set {
            if (t == null) t = value;
        }
    }
}
裸钻 2024-10-11 09:48:24

我想我已经找到了一种方法来做到这一点,如果它是在构造函数中完成的话。它也可以位于变量声明行。
如果在这些上下文之外完成,您将收到错误:无法将只读字段分配给(构造函数或变量初始值设定项除外)

public class BillLine
{
    public BillLine(Bill bill)
    {
        m_Bill = bill;
    }

    private readonly Bill m_Bill;

    public Bill Bill
    {
        get { return m_Bill; }

        //supposed to be set only in the constructor
    }

    //(...)
}

I think I achieved a way to do it, if it is done in the constructor. It could also be on the variable declaration line.
If done outside those contexts, you will receive an error: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

public class BillLine
{
    public BillLine(Bill bill)
    {
        m_Bill = bill;
    }

    private readonly Bill m_Bill;

    public Bill Bill
    {
        get { return m_Bill; }

        //supposed to be set only in the constructor
    }

    //(...)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文