属性支持值范围



这样的事情可能发生吗?我想不会,但对我来说很好

class MyClass {
    public int Foo {
        get { return m_foo; }
        set {
            // Bounds checking, or other things that prevent the use
            // of an auto-implemented property
            m_foo = value;
        }
        // Put the backing field actually *in* the scope of the property
        // so that the rest of the class cannot access it.
        private int m_foo;
    }
    void Method() {
        m_foo = 42;    // Can't touch this!
    }
}

当然,我知道这个语法是不正确的,这不会编译。为了清楚地描述我的想法,这是假设的未来。我为这个有点假设性的问题道歉,但它对程序员来说太具体了

类似的东西可以在编译器中实现,目的只有一个:只允许属性的getset访问器查看字段,本质上允许属性是自包含的(就像自动实现的属性一样),同时允许额外的获取/设置逻辑。

简短的回答是否定的,这在今天的C#中是不可能的。

我们经常收到这样的功能请求;更一般的形式是更清楚地使局部变量的生存期与其范围正交。

只是为了确保这些术语是明确的:变量是一个存储位置,可能命名为。每个变量都有一个生存期:运行时保证变量引用有效存储的时间量。名称范围是可以使用该名称的文本区域;它是一个编译时概念,而不是运行时概念。局部变量是其作用域语句块的变量。

在许多语言中,局部变量的生存期与其作用域密切相关:当控件在运行时逻辑地进入作用域时,生存期开始,当它离开作用域时结束。这在C#中是正确的,但有一些值得注意的注意事项:

  • 如果运行时可以确定这样做对当前线程上托管代码的操作没有影响,则本地的生存期可能会延长或缩短。当前线程上的其他线程(如终结器线程)和非托管代码的操作是由实现定义的。

  • 迭代器块、异步方法或匿名函数的封闭外部变量中的局部的生存期可能会延长,以匹配或超过使用它的迭代器、任务、委托或表达式树的生存期。

显然,本地的生存期和范围以任何方式联系在一起并不是的要求。如果我们可以明确地拥有具有实例或静态字段的生存期,但具有局部的范围的局部,那就太好了。C具有这一特点;您可以创建一个"静态"局部变量。C#没有。您的建议本质上是允许在具有实例生存期但其范围仅限于块的属性的块中使用局部变量。

我会把这个特性归类为"不错"。我们有一个潜在的"好"功能列表,只要你的手臂,我们没有时间实现,所以我不希望这个功能很快成为列表的首位。感谢您的反馈;它有助于我们在一定程度上优先考虑该列表。

以下是我的看法:

public class WrappedField<T>
{
    public class Internals
    {
        public T Value;
    }
    private readonly Internals _internals = new Internals();
    private readonly Func<Internals, T> _get;
    private readonly Action<Internals, T> _set;
    public T Value
    {
        get { return _get(_internals); }
        set { _set(_internals, value); }
    }
    public WrappedField(Func<Internals, T> get, Action<Internals, T> set)
    {
        _get = get;
        _set = set;            
    }
    public WrappedField(Func<Internals, T> get, Action<Internals, T> set, T initialValue)
        : this(get, set)
    {
        _set(_internals, initialValue);
    }
}

用法:

class Program
{
    readonly WrappedField<int> _weight = new WrappedField<int>(
        i => i.Value,           // get
        (i, v) => i.Value = v,  // set
        11);                    // initialValue
    static void Main(string[] args)
    {
        Program p = new Program();
        p._weight.Value = 10;
        Console.WriteLine(p._weight.Value);
    }
}

根据C#4.0语言规范。

但是,与字段不同,属性并不表示存储位置。相反,属性具有指定要在读取或写入它们的值时执行。

添加字段需要一个内存位置。所以不,这是不可能的。

如果您想避免泛型,您可以始终在私有内部类中隐藏_backingField和边界检查。您甚至可以通过使外部类成为分部类来进一步隐藏它。当然,外部和内部阶级之间必须进行一些委派,这是一件令人沮丧的事情。解释我想法的代码:

public partial class MyClass
{
    public int Property
    {
        get { return _properties.Property; }
        set { _properties.Property = value; }
    }
    public void Stuff()
    {
        // Can't get to _backingField...
    }
}
public partial class MyClass
{
    private readonly Properties _properties = new Properties();
    private class Properties
    {
        private int _backingField;
        public int Property
        {
            get { return _backingField; }
            set
            {
                // perform checks
                _backingField = value;
            }
        }
    }
}

但这是大量的代码。为了证明所有这些锅炉板的合理性,最初的问题必须相当严重。。。

没有,唯一可以在属性主体中的是getset

嗯,这很难处理,可能不是很好的性能,也不是我真正使用的东西,但从技术上讲,这是一种将后台字段与类的其他部分隔开的方式。

public class MySuperAwesomeProperty<T>
{
    private T backingField;
    private Func<T, T> getter;
    private Func<T, T> setter;
    public MySuperAwesomeProperty(Func<T, T> getter, Func<T, T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value
    {
        get
        {
            return getter(backingField);
        }
        set
        {
            backingField = setter(value);
        }
    }
}
public class Foo
{
    public MySuperAwesomeProperty<int> Bar { get; private set; }

    public Foo()
    {
        Bar = new MySuperAwesomeProperty<int>(
            value => value, value => { doStuff(); return value; });
        Bar.Value = 5;
        Console.WriteLine(Bar.Value);
    }
    private void doStuff()
    {
        throw new NotImplementedException();
    }
}

最新更新