7

以下の簡単なデモは、私がやろうとしていることを示しています。実際のプログラムでは、LINQ to SQL select式のリストを読み取っているため、オブジェクト初期化ブロックを使用する必要があります。データベースから読み取ってオブジェクトに格納したい値がありますが、オブジェクトその値に設定できる単純なプロパティがありません。代わりに、XMLデータストアがあります。

オブジェクトの初期化ブロックで拡張メソッドを呼び出すことができず、拡張メソッドを使用してプロパティをアタッチできないようです。

それで、私はこのアプローチで運が悪いのでしょうか?唯一の選択肢は、基本クラスの所有者にこのシナリオ用に変更するように説得することのようです。

BaseDataObjectをサブクラス化する既存のソリューションがありますが、これにも問題があり、この単純な例には表示されません。オブジェクトは、BaseDataObjectとして永続化および復元されます。キャストとテストは複雑になります。

public class BaseDataObject
{

    // internal data store
    private Dictionary<string, object> attachedData = new Dictionary<string, object>();

    public void SetData(string key, object value)
    {
        attachedData[key] = value;
    }

    public object GetData(string key)
    {
        return attachedData[key];
    }

    public int SomeValue { get; set; }
    public int SomeOtherValue { get; set; }

}

public static class Extensions
{
    public static void SetBarValue(this BaseDataObject dataObject,
                                        int            barValue)
    {
        /// Cannot attach a property to BaseDataObject?
        dataObject.SetData("bar", barValue);
    }
}

public class TestDemo
{

    public void CreateTest()
    {
        // this works
        BaseDataObject test1 = new BaseDataObject 
        { SomeValue = 3, SomeOtherValue = 4 };

        // this does not work - it does not compile
        // cannot use extension method in the initialiser block
        // cannot make an exension property  
        BaseDataObject test2 = new BaseDataObject { SomeValue = 3, SomeOtherValue = 4, SetBarValue(5) };
    }
}

(mattlantからの)回答の1つは、流暢なインターフェーススタイルの拡張メソッドを使用することを提案しています。例えば:

// fluent interface style
public static BaseDataObject SetBarValueWithReturn(this BaseDataObject dataObject, int barValue)
{
    dataObject.SetData("bar", barValue);
    return dataObject;
}

// this works
BaseDataObject test3 = (new BaseDataObject { SomeValue = 3, SomeOtherValue = 4 }).SetBarValueWithReturn(5);

しかし、これはLINQクエリで機能しますか?

4

6 に答える 6

5

オブジェクト初期化子は、巧妙なコンパイラを必要とするシンタックス シュガーにすぎません。現在の実装では、初期化子でメソッドを呼び出すことはできません。

var x = new BaseDataObject { SomeValue = 3, SomeOtherValue = 4 };

コンパイラを次のようにします。

BaseDataObject tempObject = new BaseDataObject();
tempObject.SomeValue = 3;
tempObject.SomeOtherValue = 4;
BaseDataObject x = tempObject;

違いは、同期の問題が発生しないことです。変数 x には、完全に割り当てられた BaseDataObject が一度に割り当てられます。初期化中にオブジェクトをいじることはできません。

オブジェクトの作成後に拡張メソッドを呼び出すことができます。

var x = new BaseDataObject { SomeValue = 3, SomeOtherValue = 4 };
x.SetBarValue()

SetBarValue を、初期化中に割り当てることができる get/set を持つプロパティに変更できます。

public int BarValue
{
    set
    {
        //Value should be ignored
    }
}

または、ファサード パターンをサブクラス化/使用して、オブジェクトにメソッドを追加することもできます。

public class DataObjectWithBarValue : BaseDataObject
{
    public void BarValue
    {
        set
        {
            SetData("bar", value);
        }
        get
        {
            (int) GetData("bar");
        }
    }
}
于 2008-09-25T09:13:19.873 に答える
4

いいえ、しかしあなたはこれを行うことができます....:

BaseDataObject test2 = (new BaseDataObject { SomeValue = 3, SomeOtherValue = 4}).SetBarValue(5);

ANLinq のように拡張機能がオブジェクトを返すようにします。

編集:これは、私が読み直して、基本クラスが第三者によって開発されたことを確認するまでは良い考えでした:別名、コードを持っていません。ここに他の人が正しい解決策を投稿しています。

于 2008-09-25T09:16:54.077 に答える
3

さらに良い:

public static T SetBarValue<T>(this T dataObject, int barValue)
        where T : BaseDataObject 
    {
        dataObject.SetData("bar", barValue);
        return dataObject;
    }

また、この拡張メソッドを BaseDataObject の派生型に使用して、キャストなしでメソッドをチェーンし、var フィールドまたは匿名型に推論されたときに実際の型を保持できます。

于 2008-09-25T09:33:52.427 に答える
1
 static T WithBarValue<T>(this T dataObject, int barValue)
        where T : BaseDataObject 
 {  dataObject.SetData("bar", barValue);    
    return dataObject;
 }

var x = new BaseDataObject{SomeValue=3, OtherValue=4}.WithBarValue(5);
于 2008-09-25T09:41:38.617 に答える
0

クラスを拡張する可能性はありますか?次に、必要なプロパティを簡単に追加できます。

それができない場合は、関心のあるクラスのプライベート インスタンスに単純にコールバックする同様のプロパティを持つ新しいクラスを作成できます。

于 2008-09-25T09:34:43.410 に答える
0

そうです、回答者から学んだ、「C# のオブジェクト初期化ブロックで拡張メソッドを使用する方法はありますか?」に対する短い答えです。は「いいえ」です。

私が直面した問題 (ここで提起したおもちゃの問題と似ていますが、より複雑な問題) を最終的に解決した方法は、次のようなハイブリッド アプローチでした。

サブクラスを作成しました。

public class SubClassedDataObject : BaseDataObject
{
    public int Bar
    {
        get { return (int)GetData("bar"); }
        set { SetData("bar", value); }
    }
}

これは LINQ で正常に動作し、初期化ブロックは次のようになります

    SubClassedDataObject testSub = new SubClassedDataObject
    { SomeValue = 3, SomeOtherValue = 4, Bar = 5 };

しかし、そもそも私がこのアプローチを好まなかった理由は、これらのオブジェクトが XML に入れられ、BaseDataObject として返され、キャスト バックが煩わしく、不要なデータ コピーになり、コピーを 2 つ置くことになるからです。プレイ中の同じオブジェクトの。

コードの残りの部分では、サブクラスを無視して拡張メソッドを使用しました。

    public static void SetBar(this BaseDataObject dataObject, int barValue)
    {
        dataObject.SetData("bar", barValue);
    }
    public static int GetBar(this BaseDataObject dataObject)
    {
        return (int)dataObject.GetData("bar");
    }

そして、それはうまく機能します。

于 2008-09-25T21:01:02.183 に答える