2

値がデフォルト値と等しい場合にオブジェクトまたは構造体に値を設定するジェネリック拡張メソッドを作成したいと思います。

だから私は次のコードを持っています:

public static void setIfNull<T>(this T i_ObjectToUpdate, T i_DefaultValue)
{
    if (EqualityComparer<T>.Default.Equals(i_ObjectToUpdate, default(T)))
    {
        i_ObjectToUpdate = i_DefaultValue;
    }
}

呼び出しの例を次に示します。

public OrganizationalUnit CreateOrganizationalUnit(OrganizationalUnit i_UnitToCreate)
{
    i_UnitToCreate.EntityCreationDate.setIfNull(DateTime.Now); //Here is a call
    i_UnitToCreate.EntityLastUpdateDate.setIfNull(DateTime.Now); //And another one
    m_Context.DomainEntities.Add(i_UnitToCreate);
    return i_UnitToCreate;
}

それが何か関係があるかどうかはわかりませんが、エンティティフレームワークとMVCを使用しています。

デバッガーで実際に何が起こるか拡張メソッドの行が機能してi_ObjectToUpdate = i_DefaultValue;いて値が変更されていることがわかりますが、デバッガーが拡張メソッドを終了すると、の値は変更されないi_UnitToCreate.EntityCreationDateままであることがわかります。

何がうまくいかなかったのか?

4

3 に答える 3

2

コードには2つの参照があります。1つはi_UnitToCreate.EntityCreationDate、メモリ内のあるアドレスを指すものです。その他はi_ObjectToUpdate拡張メソッドにあり、最初はそのアドレスも指します(メソッドへの参照を渡すときにアドレスのコピーをi_UnitToCreate.EntityCreationDate作成します)。後で、メモリ内の他のオブジェクトを指すように2番目の参照を変更しますが、最初の参照は独立しているため、変更されません。

回避策

public static void SetIfDefault<T, TProperty>(this T arg,
    Expression<Func<T, TProperty>> propertySelector, TProperty value)
{
    TProperty currentValue = propertySelector.Compile()(arg);
    EqualityComparer<TProperty> comparer = EqualityComparer<TProperty>.Default;
    if (!comparer.Equals(currentValue, default(TProperty)))
        return;
    PropertyInfo property = (PropertyInfo)((MemberExpression)propertySelector.Body).Member;
    property.SetValue(arg, value);
} 

式を使用して、プロパティセレクター(プロパティ値ではない)を拡張メソッドに渡すことができます。コンパイルされた式から値を取得します。デフォルト値の場合は、リフレクションを使用して(PropertyInfoメンバー式をキャストすることで簡単に取得できます)、新しい値を設定します。使用法:

i_UnitToCreate.SetIfDefault(x => x.EntityCreationDate, DateTime.Now);
i_UnitToCreate.SetIfDefault(x => x.EntityLastUpdateDate, DateTime.Now);

PS私の最初の答えについて1つの意見があります-T参照値のコピーについて話すとき、私は参照型としてジェネリック型について考えました。値型(DateTimeとして)を使用すると、オブジェクト全体がコピーされます。結果は変わりませんが(新しい値を割り当てることはできません)、言及する必要があります。

于 2012-11-24T09:43:57.807 に答える
0

問題は、拡張メソッド内i_ObjectToUpdateで、ヒープ内にあるオブジェクトへのローカル参照(ポインター)です。設定する場合:

i_ObjectToUpdate = i_DefaultValue

ヒープ内のオブジェクトを実際に変更するのではなく、を指すオブジェクトへのローカル参照i_ObjectToUpdateポイントをi_DefaultValue作成しますが、拡張メソッドのスコープ内でのみ変更します。呼び出しコンテキストでの元の参照:

i_UnitToCreate.EntityCreationDate
i_UnitToCreate.EntityLastUpdateDate

変更されません。

この問題を回避するには、実際にポインタを拡張メソッドへのポインタに渡す必要があります。これは、refキーワードを追加することによって行われます。

    public static void setIfNull<T>(this ref T i_ObjectToUpdate, T i_DefaultValue)
    {
        if (EqualityComparer<T>.Default.Equals(i_ObjectToUpdate, default(T)))
        {
            i_ObjectToUpdate = i_DefaultValue;
        }
    }

残念ながら、拡張メソッドはrefキーワードをサポートしていません。別の方法は、メソッドを標準の静的メソッドとして使用することです。

    public static void setIfNull<T>(ref T i_ObjectToUpdate, T i_DefaultValue)
    {
        if (EqualityComparer<T>.Default.Equals(i_ObjectToUpdate, default(T)))
        {
            i_ObjectToUpdate = i_DefaultValue;
        }
    }

そしてそれをそのように呼ぶ:

Program.setIfNull(i_UnitToCreate.EntityCreationDate, DateTime.Now);

理想的には、nullチェックのみを行う別の拡張メソッドを作成してから、変数を自分で設定できます。

    public static T IfNull<T>(this T i_ObjectToUpdate, T i_DefaultValue)
    {
        if (EqualityComparer<T>.Default.Equals(i_ObjectToUpdate, default(T)))
        {
            return i_DefaultValue;
        }
        return i_ObjectToUpdate;
    }

そしてそれを経由して呼び出す:

i_UnitToCreate.EntityLastUpdateDate = _UnitToCreate.EntityLastUpdateDate.IfNull(DateTime.Now);

これは(参照型を使用する場合)と同等です:

i_UnitToCreate.EntityLastUpdateDate = i_UnitToCreate.EntityLastUpdateDate ?? DateTime.Now;
于 2012-11-24T09:51:03.447 に答える
0

より良いアプローチは、C#4.0??演算子を使用することだと思います。

   var nullable;

   . . . 

   nullable = nullable ?? "defaultValue";

何を達成しようとしていますか?私自身の個人的な作業習慣では、拡張メソッドをIDictionary作成した参照キーが存在しない場合(例外をスローする場合)にC#オブジェクトが例外をスローすることに非常に不満を感じました。dict['not here']

public static TValue Safeget<TValue, TKey>(this IDictionary<TKey, TValue> arg, TKey key, TValue defaultValue = default(TValue))
        {
            if (arg.ContainsKey(key))
            {
                return arg[key];
            }

            return defaultValue;
        }

これはあなたが求めているようなものですか?

于 2012-11-24T10:29:18.063 に答える