2012-01-31 6 views
1

すべての型の安全なenumに対してコンバーターを記述することなく、Stringを型安全なenumクラス(CountryIso)に変換するTypeConverterが必要です約作る。MCV環境で使用する汎用ジェネリックTypeConverterでInvalidCastExceptionが発生しました。

私は仕事に以下を得ることができますが:

CountryIso cI = (CountryIso) "1"; 

私はちょうどそれがジェネリックで動作させることはできません!次のサンプルは動作しませんが、なぜですか?

public class ExplicitCastConverter<T>: TypeConverter 
{ 
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 
    { 
     // Always true: the type determines if a cast is available or not 
     return true; 
    } 

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 
    { 
     String dummy = (String) value; 
     //CountryIso tst = (CountryIso) value; // Allowed, no problem casting 
     //CountryIso tst = (CountryIso) dummy; // Allowed, no problem casting 
     //var dum_001 = (T) ((String) value); // Does not compile 
     //var dumdum = (T) value; // Invalid case exception 
     //var hoot = (T) Convert.ChangeType(value, typeof (T)); // Invalid cast exception 
     return null; 
    } 
} 

プロバイダは次のとおりです。:

TypeDescriptor.AddProvider(new ExplicitCastDescriptionProvider<CountryIso>(), typeof(CountryIso)); 
var descriptor = TypeDescriptor.GetConverter(typeof(CountryIso)); 
var result = descriptor.ConvertFrom("1"); 

私は現在、汎用のTypeConverterの実装を持つ

//thanks: http://groups.google.com/group/wpf-disciples/browse_thread/thread/9f7bb40b7413fcd 
public class ExplicitCastDescriptionProvider<T> : TypeDescriptionProvider //where T:TypeSafeEnum 
{ 
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) 
    { 
     return new ImplicitCastDescription<T>(); 
    } 
} 

public class ImplicitCastDescription<T>: CustomTypeDescriptor //where T:TypeSafeEnum 
{ 
    public override TypeConverter GetConverter() 
    { 
     return new ExplicitCastConverter<T>(); 
    } 
} 

私が持っているタイプセーフ列挙実装CountryIso(感謝StackOverflowの!):

public sealed class CountryIso: TypeSafeEnum 
{ 
    private static readonly Dictionary<int, CountryIso> InstanceDict = new Dictionary<int, CountryIso>(); 

    public static readonly CountryIso NL = new CountryIso(1, "NL", "Netherlands"); 
    public static readonly CountryIso BE = new CountryIso(2, "BE", "Belgium"); 

    private CountryIso(int value, String name, String description): base(value,name,description) 
    { 
     InstanceDict.Add(value, this); 
    } 

    public static Dictionary<int, CountryIso> Instances 
    { 
     get { return new Dictionary<int, CountryIso>(InstanceDict); } 
    } 

    public static explicit operator CountryIso(String i) 
    { 
     int index; 
     return Int32.TryParse(i,out index) ? InstanceDict[index] : null; 
    } 
} 
TypeSafeEnumから継承

public class TypeSafeEnum 
{ 
    protected TypeSafeEnum(int value, String name, String description) 
    { 
     Name = name; 
     Value = value; 
     Description = description; 
    } 

    public int Value{ get; private set; } 
    public String Name { get; private set; } 
    public String Description { get; private set; } 
} 

答えて

0

一つのオプション:使用反射

は問題がCountryIsoメンバーと(ほとんど)キャスト演算子の静的な性質です。これにより、総称型変換器がCountryIso型の安全な列挙型をキャストできることを知ることができるように、青写真の定義を防止する。また、 'down'をキャストすることはできません。TypeSafeEnumはCountryIsoになることはありません。論理的ですが、助けになりません。鋳造方法を定義する汎用的なインタフェースを導入

  1. [反射使い方]

    public interface ICast<out T> 
    { 
        T Cast(String obj); 
    } 
    
  2. はCountryIso

    public sealed class CountryIso: TypeSafeEnum , ICast<CountryIso> 
    
  3. へのインタフェースを適用contstraintとしてのインタフェースを追加します。にコンバータクラス

    public class ExplicitCastConverter<T>: TypeConverter where T: ICast<T> 
    
  4. は私CountryIsoに(非static)キャスト法を追加します。

    private static readonly CountryIso DefaultTypeSafeEnum = new CountryIso(
        -1, 
        null, 
        null 
    ); 
    
  5. public new CountryIso Cast(String obj) 
    { 
        int index; 
        return Int32.TryParse(obj, out index) ? InstanceDict[index] : null; 
    } 
    
  6. は私のタイプセーフな-列挙にデフォルトの静的メンバを追加しました

  7. ConvertFrom(..)Converterクラスに:

    T defaultMember = (T)typeof(T).GetField(
        "DefaultTypeSafeEnum", 
        BindingFlags.NonPublic | BindingFlags.Static 
    ).GetValue(null); 
    
    return defaultMember.Cast((String) value); 
    

[タイプセーフ列挙型セキュリティ]

それはのためにInstanceDictを使用する場合は特に(反射によってCountryIsoの新しいインスタンスを挿入&を作成するまだ可能です簡単なインスタンスアクセス!)いくつかのサンプルコード:

ConstructorInfo ci = typeof (T).GetConstructor(
    BindingFlags.NonPublic|BindingFlags.Instance, 
    null, 
    CallingConventions.Standard, 
    new [] {typeof (int), typeof (String), typeof (String)}, 
    new ParameterModifier[0] 
); 

CountryIso countryIso = (CountryIso) ci.Invoke(new object[]{30, "ZB", "Zanzibar"}); 

私は今プライベートInstanceDictメンバの使用を考えますセキュリティホール(私は外部のAPIをプログラミングしていないので、大きなものではありませんが、まだ...)

関連する問題