ジェネリックメソッド、アンボクシングNULL可能列挙型
質問
私は、次の拡張メソッドを作った...
public static class ObjectExtensions
{
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
return (T) pObject;
}
}
...私は、例えばのために使用されそうのようなデータを読みます:
string field = datareader["column"].As("default value when null")
しかし、私は箱入りの値からNULL可能列挙型にキャストしたいとき、それは動作しません。私が思い付くことができる最高のは、この(仕事をしません乱雑WIPコード)であった。
public static class ObjectExtensions
{
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
var lType = typeof (T);
if (!IsNullableEnum(lType))
return (T) pObject;
var lEnumType = Nullable.GetUnderlyingType(lType);
var lEnumPrimitiveType = lEnumType.GetEnumUnderlyingType();
if (lEnumPrimitiveType == typeof(int))
{
var lObject = (int?) pObject;
return (T) Convert.ChangeType(lObject, lType);
}
throw new InvalidCastException();
}
private static bool IsNullableEnum(Type pType)
{
Type lUnderlyingType = Nullable.GetUnderlyingType(pType);
return (lUnderlyingType != null) && lUnderlyingType.IsEnum;
}
}
使用方法:
public enum SomeEnum {Value1, Value2};
object value = 1;
var result = value.As<SomeEnum?>();
それがNULL可能で列挙型へのInt32にキャストしようとすると、は、現在のエラーがInvalidCastExceptionがあります。これは[OK]を私の推測ですが、私はそれを行うことができるか他にないアイデアをしましたか?私はNULL可能列挙型Tのインスタンスを作成し、値を代入しようとしたが、私はこれを行うことができます正確にどのように立ち往生しています。
誰でもアイデアやこれを解決するためのより良い方法はありますか?それは一般的な方法でそれを解決することも可能ですか?私はその上の検索のかなり多くをやったが、私は便利なものを見つけていませんでした。
解決
あなたが必要とするNULL可能タイプのコンストラクタを呼び出すことによってそれを行うことができます。このように:
Type t = typeof(Nullable<>).MakeGenericType(lEnumType);
var ctor = t.GetConstructor(new Type[] { lEnumType });
return (T)ctor.Invoke(new object[] { pObject });
他のヒント
私はそれが働いて得ることができたと誰もが興味を持っている場合は、ここで修正版です。
は、public static class ObjectExtensions
{
private static Dictionary<Type, ConstructorInfo> _NullableEnumCtor = new Dictionary<Type, ConstructorInfo>();
public static T As<T>(this object pObject)
{
return As(pObject, default(T));
}
public static T As<T>(this object pObject, T pDefaultValue)
{
if (pObject == null || pObject == DBNull.Value)
return pDefaultValue;
var lObjectType = pObject.GetType();
var lTargetType = typeof(T);
if (lObjectType == lTargetType)
return (T) pObject;
var lCtor = GetNullableEnumCtor(lTargetType);
if (lCtor == null)
return (T) pObject;
return (T)lCtor.Invoke(new[] { pObject });
}
private static ConstructorInfo GetNullableEnumCtor(Type pType)
{
if (_NullableEnumCtor.ContainsKey(pType))
return _NullableEnumCtor[pType];
var lUnderlyingType = Nullable.GetUnderlyingType(pType);
if (lUnderlyingType == null || !lUnderlyingType.IsEnum)
{
lock (_NullableEnumCtor) { _NullableEnumCtor.Add(pType, null); }
return null;
}
var lNullableType = typeof(Nullable<>).MakeGenericType(lUnderlyingType);
var lCtor = lNullableType.GetConstructor(new[] { lUnderlyingType });
lock (_NullableEnumCtor) { _NullableEnumCtor.Add(pType, lCtor); }
return lCtor;
}
}
しかし、NULL可能な列挙型の追加検査/コード他のすべてのタイプのためにパフォーマンスが痛いです。拡張メソッドが遅く〜2-3だった前に、今では〜10〜15倍です。上記のコードを使用してそれを1000000(百万円)回をやってます:
ボックス化解除INT:4msの
拡張メソッドを使用してアンボクシングint型:59ms(NULL可能な列挙型の世話をせずに前:12msの)
NULL可能で列挙型にアンボクシング:5msの
拡張メソッドを使用してNULL可能な列挙にアンボクシング:3382ms
だから、パフォーマンスが重要な場合、これらのメソッドは、最初の選択肢ではありませんこれらの数字を見て - 。NULL可能な列挙型のためにそれを使用した場合、少なくともません。