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 には具体的すぎます。

このようなものは、1 つの目的を果たすコンパイラで実装できます: プロパティgetsetアクセサーのみがフィールドを参照できるようにし、基本的にプロパティを自己完結型にする (自動実装プロパティのように) 一方で、追加の get/set ロジックを許可します。 .

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

まあ、対処するのはかなり難しく、おそらくあまりパフォーマンスが高くなく、私が実際に使用するものではありませんが、技術的には、クラスの他のメンバーからバッキングフィールドを隠す方法です。

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 に答える
1

いいえ、プロパティの本体内にあることができるのはgetandだけsetです。

于 2012-08-23T19:33:30.037 に答える