Ответ 1
Я считаю, что если вы просто делаете
prop.SetValue(obj,null,null);
Если это valueetype, он установит значение по умолчанию, если это ссылочный тип, он установит его равным нулю.
Это действительно потрясает меня сегодня. Я уверен, что это не так сложно, но у меня есть объект System.Reflection.PropertyInfo. Я хочу установить его значение на основе результата поиска базы данных (подумайте, что ORM, отображая столбец обратно в свойство).
Моя проблема в том, что возвращаемое значение DB - DBNull, я просто хочу установить значение свойства по умолчанию, то же самое, что и вызов:
value = default(T); // where T is the type of the property.
Однако метод default() не будет компилироваться, если вы дадите ему Type, который есть у меня:
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 (Тип) недействителен. Я также думал о том, чтобы делать:
object myObj = ???;
PropertyInfo myPropInf = ???;
myPropInf.SetValue(myObj, Activator.CreateInstance(myPropInf.PropertyType), null);
Однако, если Type является строкой, которая присваивает значение "new String()", но я действительно хочу "null", что и будет возвращено "default (string)".
Так что мне здесь не хватает? Я полагаю, что действительно взломанным способом было бы создать новый экземпляр myObj Type и скопировать его свойство, но это просто кажется глупым...
object myObj = ???;
PropertyInfo myPropInf = ???;
var blank = Activator.CreateInstance(myObj.GetType());
object defaultValue = myPropInf.GetValue(blank, null);
myPropInf.SetValue(myObj, defaultValue, null);
Я бы предпочел не тратить память на создание совершенно нового экземпляра, просто чтобы получить значение по умолчанию для свойства. Кажется очень расточительным.
Любые идеи?
Я считаю, что если вы просто делаете
prop.SetValue(obj,null,null);
Если это valueetype, он установит значение по умолчанию, если это ссылочный тип, он установит его равным нулю.
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
"null" трюк установит значение ноль для этого типа, что не обязательно совпадает с значением по умолчанию для свойства. Во-первых, если это новый объект, почему бы просто не оставить его в покое? В качестве альтернативы используйте TypeDescriptor
:
PropertyDescriptor prop = TypeDescriptor.GetProperties(foo)["Bar"];
if (prop.CanResetValue(foo)) prop.ResetValue(foo);
Это относится как к шаблонам [DefaultValue]
, так и к Reset{name}()
(как используется связывание и сериализация), что делает его очень универсальным и повторно используемым.
Если вы делаете много этого, вы можете также повысить производительность, используя TypeDescriptor
вместо отражения, повторно используя PropertyDescriptorCollection
и используя HyperDescriptor (тот же код, но намного быстрее, чем либо refleion, либо raw TypeDescriptor
).
Попробуйте следующие методы, которые я написал и протестировал против тысяч типов:
/// <summary>
/// [ <c>public static T GetDefault< T >()</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<T>"/>
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, конечно, избыточна для С#, так как вы можете просто использовать ключевое слово по умолчанию (T).
Наслаждайтесь!
Это более полированная версия, которая поддерживает функциональность .NET Runtime без добавления лишнего настраиваемого кода.
ПРИМЕЧАНИЕ. Этот код написан для .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)