2012-08-23 140 views
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#。我对这个有点假设的问题表示歉意,但对程序员来说太具体了。

类似这样的东西可以在编译器中实现,它可以用于一个目的:只允许属性的getset访问器查看字段,实质上允许该属性是自包含的(作为自动实现的属性),而允许额外的获取/设置逻辑。

+2

这是一个简单的是/否的问题(因为那样很** **无),或者你想替代解决这个‘问题’? –

+0

我只是好奇你想通过这样做达到什么目的? –

+0

备用解决方案将会非常有趣。强制使用属性而不是类中的变量可能是一个很好的功能。因为这一定是目标,对吧? –

回答

31

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

我们经常得到这样的功能请求;这是更一般形式的一个很好的功能。更一般的形式是为了更清楚地使寿命的本地可变正交的范围

只是为了确保这些条款是明确的:a 变量是一个存储位置,可能命名。每个变量具有寿命:的时间在运行时,其中可变保证指有效的存储量。一个名称范围是在其中可以使用该名称的文本的区域;这是一个编译时的概念,而不是运行时的概念。一个当地变量是一个变量,其范围语句块

在许多语言中,一个局部变量的寿命密切相关的其范围:当控制逻辑进入所述范围在运行时,寿命开始和当它离开范围,寿命结束。这是在C#中真正有一些明显的警告:

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

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

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

我想这个功能为“好”的分类。只要您的手臂没有时间执行,我们就会列出潜在的“好”功能列表,因此我不希望这个功能能够在短时间内将它列入列表顶部。感谢您的反馈,但;它有助于我们优先考虑该列表。

+1

问题:对C#语言的设计有一个简单的普遍改变,它会使变量去耦生命期和范围?你指出C中的属性范围变量(OP的问题)和静态局部变量(你的例子)是将范围的生命期解耦的具体情况,但我想不出一般情况是什么。 – phoog

1

不可以,唯一可以在物业主体内的是getset

2

根据C#4.0语言规范。

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

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

+1

我明白你在说什么,但这并不适用于此。我只关心字段的词汇范围,而不关心它存储在内存中的地方。当然,属性可以归结为'_get()'和'_set()'方法,因此该字段仍将包含在类的上下文中。 –

+0

@JonathonReinhart - 如果包含类型不是属性,那么该概念提供了什么效用?包含的类型,例如班级仍然可以访问私人领域。这也提出了财产是否可以被视为包含类型的问题。 –

+2

我只是说,类似这样的东西可以在编译器中实现,它可以达到一个目的:只允许属性的get和set访问器查看该字段,实质上允许该属性是自包含的自动实现的属性),同时允许额外的逻辑。 –

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(); 
    } 
} 
+0

没有什么能够阻止您在MySuperAwesomeProperty类中设置后台字段。这也忽略了原始问题国际海事组织的观点。 – user981225

+0

@ user981225属性类的全部要点是它将被密封并包含在某个地方,不会被修改。其他类只是简单地使用它,而不是实际值的属性,以允许自定义getter/setter而无需访问后台字段。它不会阻止因反射而导致的修改,但在该级别,即使是普通属性的后台字段也可以访问。这不是原始问题的重点? – Servy

+0

我的歉意,你是对的。如果你编辑帖子,以便我可以upvote,我会的。 – user981225

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); 
    } 
} 
+0

您也可以实现自定义隐式转换,以便用户代码不会受到影响,您不必使用varname.Value来设置和获取属性的值 – Sonia

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

但是这是很多代码。为了证明所有锅炉板,原来的问题已相当严重......

+0

为什么有人想要避免泛型? – Grozz

+0

@Grozz我不知道。我当然不会。我确实提出了Servy的回答。但是,有两个答案已经是基于泛型的,所以我认为“什么是h ** l”:-) –