2012-08-23 139 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」:-) –