物业支持价值范围

时间:2012-08-23 19:31:18

标签: c# properties

这样的事情可能吗?我假设没有,但对我来说很好看

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访问器查看该字段,基本上允许该属性是自包含的(如自动实现的属性是),同时允许额外的获取/设置逻辑。

6 个答案:

答案 0 :(得分:31)

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

我们经常收到这样的功能请求;它的更通用的形式是一个很好的功能。更通用的形式是更明确地使本地变量的生命周期与其范围正交。

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

在许多语言中,局部变量的生命周期与其范围密切相关:当控件在运行时逻辑上进入作用域时,生命周期开始,当它离开作用域时,生命周期结束。在C#中也是如此,有一些值得注意的警告:

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

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

显然,不是要求本地的生命周期和范围以任何方式捆绑在一起。如果我们可以明确地拥有具有实例或静态字段的生命周期的本地人,但是本地的范围,那将是很好的。 C有这个功能;你可以创建一个“静态”局部变量。 C#没有。您的提议主要是允许属性块中的局部变量具有实例的生命周期,但其范围仅限于块。

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

答案 1 :(得分: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);
    }
}

答案 2 :(得分:2)

根据C#4.0语言规范。

  

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

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

答案 3 :(得分: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;
            }
        }
    }
}

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

答案 4 :(得分:1)

不,可能属于该属性的唯一内容是getset

答案 5 :(得分: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();
    }
}