18

这样的事情可能吗?我假设不是,但对我来说看起来不错

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

当然我知道这个语法是不正确的,这不会编译。为了清楚地描绘我的想法,它是假设的未来 C#。对于这个有点假设的问题,我深表歉意,但它对 Programmers.SE 来说太具体了。

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

4

6 回答 6

31

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

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

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

在许多语言中,局部变量的生命周期与其范围密切相关:当控制在运行时逻辑地进入范围时,生命周期开始,当它离开范围时,生命周期结束。这在 C# 中是正确的,但有一些值得注意的警告:

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

  • 迭代器块、异步方法或匿名函数的封闭外部变量中的局部变量的生命周期可以扩展为匹配或超过使用它的迭代器、任务、委托或表达式树的生命周期.

显然,不需要任何方式将本地的生命周期和范围捆绑在一起。如果我们可以显式地让局部变量具有实例或静态字段的生命周期,但具有局部变量的范围,那就太好了。C有这个特点;您可以制作一个“静态”局部变量。C# 没有。您的建议本质上是允许在具有实例生命周期但其范围仅限于块的属性块内使用局部变量。

我会将此功能归类为“不错”。我们有一个潜在的“好”功能列表,只要您的手臂,我们没有时间实施,所以我不希望这个功能很快成为列表的顶部。感谢您的反馈;它有助于我们在一定程度上优先考虑该列表。

于 2012-09-25T16:16:10.813 回答
6

这是我的看法:

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);
    }
}
于 2012-08-23T20:19:46.513 回答
2

根据 C# 4.0 语言规范。

但是,与字段不同,属性不表示存储位置。相反,属性具有指定在读取或写入其值时要执行的语句的访问器。

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

于 2012-08-23T19:54:21.393 回答
2

如果你想避免泛型,你总是可以隐藏_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;
            }
        }
    }
}

但这是很多代码。为了证明所有这些样板的合理性,最初的问题必须非常严重......

于 2012-08-23T20:39:53.017 回答
1

不,唯一可以在属性主体内的是getand set

于 2012-08-23T19:33:30.037 回答
1

好吧,它很难处理,可能不是很高效,也不是我真正会使用的东西,但从技术上讲,它是一种将支持领域与班级其他人隔开的方式。

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();
    }
}
于 2012-08-23T20:11:35.137 回答