web-dev-qa-db-ja.com

.NET-反映されたPropertyInfoのデフォルト値を取得します

これは本当に私を困らせています。難しいことではないと思いますが、System.Reflection.PropertyInfoオブジェクトがあります。データベースルックアップの結果に基づいて値を設定したい(ORMを考えて、列をプロパティにマッピングし直す)。

私の問題は、DBの戻り値がDBNullの場合、次の呼び出しと同じように、プロパティ値をデフォルトに設定したいだけです。

value = default(T);  // where T is the type of the property.

ただし、私が持っているTypeを指定した場合、default()メソッドはコンパイルされません。

object myObj = ???; // doesn't really matter. some arbitrary class.
PropertyInfo myPropInf = ???; // the reflection data for a property on the myObj object.
myPropInf.SetValue(myObj, default(myPropInf.PropertyType), null);

上記はコンパイルされません。 default(Type)が無効です。私もすることを考えました:

object myObj = ???;
PropertyInfo myPropInf = ???;
myPropInf.SetValue(myObj, Activator.CreateInstance(myPropInf.PropertyType), null);

ただし、Typeが文字列の場合、値 "new String()"が割り当てられますが、 "default(string)"が返す "null"が本当に必要です。

ここで何が欠けているのですか?本当にハックな方法は、myObjのTypeの新しいインスタンスを作成してプロパティをコピーすることだと思いますが、それはばかげているように思えます...

object myObj = ???;
PropertyInfo  myPropInf = ???;
var blank = Activator.CreateInstance(myObj.GetType());
object defaultValue = myPropInf.GetValue(blank, null);
myPropInf.SetValue(myObj, defaultValue, null);

プロパティのデフォルトを取得するためだけに、まったく新しいインスタンスを作成するためにメモリを無駄にしたくありません。非常に無駄が多いようです。

何か案は?

55
CodingWithSpike

あなたがやれば

prop.SetValue(obj,null,null);

値タイプの場合はデフォルト値に設定され、参照タイプの場合はnullに設定されます。

52
BFree
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
47
Darin Dimitrov

「null」トリックは、タイプのzero値に設定します。これは、プロパティのデフォルトと必ずしも同じではありません。まず、それが新しいオブジェクトである場合、それをそのままにしないのはなぜですか?または、TypeDescriptorを使用します。

_PropertyDescriptor prop = TypeDescriptor.GetProperties(foo)["Bar"];
if (prop.CanResetValue(foo)) prop.ResetValue(foo);
_

これは_[DefaultValue]_とReset{name}()パターンの両方を尊重し(バインドとシリアル化で使用される)、非常に用途が広く、再利用可能になります。

これをたくさん実行している場合、TypeDescriptorを再利用して HyperDescriptor (同じコードを使用)することで、リフレクションの代わりにPropertyDescriptorCollectionを使用してパフォーマンスを向上させることもできます。ただし、refletionまたはraw TypeDescriptor)よりもはるかに高速です

29
Marc Gravell

以下の方法を試してみてください。これらは何千ものタイプに対して作成およびテストしたものです。

    /// <summary>
    /// [ <c>public static T GetDefault&lt; T &gt;()</c> ]
    /// <para></para>
    /// Retrieves the default value for a given Type
    /// </summary>
    /// <typeparam name="T">The Type for which to get the default value</typeparam>
    /// <returns>The default value for Type T</returns>
    /// <remarks>
    /// If a reference Type or a System.Void Type is supplied, this method always returns null.  If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception.
    /// </remarks>
    /// <seealso cref="GetDefault(Type)"/>
    public static T GetDefault<T>()
    {
        return (T) GetDefault(typeof(T));
    }

    /// <summary>
    /// [ <c>public static object GetDefault(Type type)</c> ]
    /// <para></para>
    /// Retrieves the default value for a given Type
    /// </summary>
    /// <param name="type">The Type for which to get the default value</param>
    /// <returns>The default value for <paramref name="type"/></returns>
    /// <remarks>
    /// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null.  If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception.
    /// </remarks>
    /// <seealso cref="GetDefault&lt;T&gt;"/>
    public static object GetDefault(Type type)
    {
        // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
        if (type == null || !type.IsValueType || type == typeof(void))
            return null;

        // If the supplied Type has generic parameters, its default value cannot be determined
        if (type.ContainsGenericParameters)
            throw new ArgumentException(
                "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
                "> contains generic parameters, so the default value cannot be retrieved");

        // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct), return a 
        //  default instance of the value type
        if (type.IsPrimitive || !type.IsNotPublic)
        {
            try
            {
                return Activator.CreateInstance(type);
            }
            catch (Exception e)
            {
                throw new ArgumentException(
                    "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " +
                    "create a default instance of the supplied value type <" + type +
                    "> (Inner Exception message: \"" + e.Message + "\")", e);
            }
        }

        // Fail with exception
        throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type + 
            "> is not a publicly-visible type, so the default value cannot be retrieved");
    }

GetDefaultの最初の(汎用)バージョンは、もちろんdefault(T)キーワードを使用するだけなので、C#では冗長です。

楽しい!

13
Mark Jones

私はこれが古い投稿であることを知っていますが、私はDarin Dimitrovの答えにこのひねりを加えたのが好きです。最初にDefualtValue属性をチェックし、次に Darin Dimitrovの回答 を使用します。

public static object GetDefaultValueForProperty(this PropertyInfo property)
    {
        var defaultAttr = property.GetCustomAttribute(typeof(DefaultValueAttribute));
        if (defaultAttr != null)
            return (defaultAttr as DefaultValueAttribute).Value;

        var propertyType = property.PropertyType;
        return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
    }
0
rahicks

これは、不必要なカスタムコードを追加せずに.NETランタイムの機能を維持する、より洗練されたバージョンです。

注:このコードは.NET 3.5 SP1用に書かれています

namespace System {

public static class TypedDefaultExtensions {

    public static object ToDefault(this Type targetType) {

        if (targetType == null)
            throw new NullReferenceException();

        var mi = typeof(TypedDefaultExtensions)
            .GetMethod("_ToDefaultHelper", Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic);

        var generic = mi.MakeGenericMethod(targetType);

        var returnValue = generic.Invoke(null, new object[0]);
        return returnValue;
    }

    static T _ToDefaultHelper<T>() {
        return default(T);
    }
}

}

使用法:

PropertyInfo pi; // set to some property info
object defaultValue = pi.PropertyType.ToDefault();

public struct MyTypeValue { public int SomeIntProperty { get; set; }
var reflectedType = typeof(MyTypeValue);
object defaultValue2 = reflectedType.ToDefault();

ラシャド・リベラ(OmegusPrime.com)

0
RashadRivera