2011-03-07 60 views
4

我有一組值轉換器將字符串轉換爲各自的類型。我有一個工廠,負責根據需要創建這些轉換器。我試圖保持工廠和轉換器通用,但我遇到了一些問題。直到我在工廠調用.Create方法時,我才知道該類型,所以我需要能夠將該類型作爲參數傳遞。麻煩的是,那麼,我的.Create方法認爲我正在尋找ValueConverter<Type>,而不是像ValueConverter<int>這樣更合適的值轉換器。我錯過了一些東西,或者甚至完全錯誤。創建通用類的通用工廠

這裏有一些我的轉換器和接口:

public interface IValueConverter<T> 
{ 
    T Convert(object objectToConvert); 
} 

public class IntValueConverter : IValueConverter<int> 
{ 
    public int Convert(object objectToConvert) 
    { 
     return System.Convert.ToInt32(objectToConvert); 
    } 
} 

public class DateTimeValueConverter : IValueConverter<DateTime> 
{ 
    public DateTime Convert(object objectToConvert) 
    { 
     return System.Convert.ToDateTime(objectToConvert); 
    } 
} 

然後,我有一個工廠這樣的:

public class ValueConverterFactory : IValueConverterFactory 
{ 
    private readonly IUnityContainer _container; 

    public ValueConverterFactory(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public IValueConverter<T> Create<T>(T type) 
    { 
     return _container.Resolve<IValueConverter<T>>(); 
    } 
} 

統一配置是這樣的:

Container.RegisterType<IValueConverter<int>, IntValueConverter>(); 
Container.RegisterType<IValueConverter<DateTime>, DateTimeValueConverter>(); 

我需要能夠這樣稱呼工廠:

var objectType = someObj.GetType(); 
var valueConverter = _valueConverterFactory.Create(objectType); 
+0

你是怎麼調用Create方法的?問題可能是IoC的東西,我從來沒有使用它,也不能說。但一個簡單的工廠工作,即我可以做以下事情:IConverter myConverter = factory.Create (typeof(int)); – Sisyphus

+0

我不能在泛型方法調用中指定我的類型,因爲直到運行時才知道類型。 –

+0

不知道你可以做你想做的事情。這種想法不,但是......你說你直到運行時才知道類型。但要甚至使用轉換,你必須說int myVar = Convert ...或者你正在嘗試var myVar = Convert?由於可讀性和其他方面(我使用nhib而非LINQ等),我不喜歡隱式類型的風扇,所以我不太熟悉使用隱式類型,但據我所知,編譯器必須能夠以某種方式確定類型。你不能用它來完全避免打字。我沒有看到編譯器如何從您的預期實現中推斷出類型? – Sisyphus

回答

0

我不知道這是否是你問什麼,但我發現在犀牛冰屋框架這個優雅和短代碼:ConversionUtil.cs - 它轉換字符串到任何類型的...

public static object ConvertTo(Type type, string inject) 
    { 
     if (inject == null) 
     { 
      return type.IsValueType ? Activator.CreateInstance(type) : null; 
     } 

     if (type.IsInstanceOfType(inject)) 
     { 
      return inject; 
     } 
     else if (type == typeof(int)) 
     { 
      int temp; 
      if (int.TryParse(inject, out temp)) 
       return temp; 
      return null; 
     } 
     else if (typeof(IConvertible).IsAssignableFrom(type)) 
     { 
      return Convert.ChangeType(inject, type); 
     } 

     //Maybe we have a constructor that accept the type? 
     ConstructorInfo ctor = type.GetConstructor(new Type[] { inject.GetType() }); 
     if (ctor != null) 
     { 
      return Activator.CreateInstance(type, inject); 
     } 

     //Maybe we have a Parse method ?? 
     MethodInfo parseMethod = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public); 
     if (parseMethod != null) 
     { 
      return parseMethod.Invoke(null, new object[] { inject }); 
     } 

     throw new ArgumentException(string.Format(
      "Cannot convert value '{0}' of type '{1}' to request type '{2}'", 
      inject, 
      inject.GetType(), 
      type)); 
    } 
+0

不是。這會返回一個對象,我真的想在對象轉換後避免強制轉換。我也希望能夠在不破壞封裝的情況下添加轉換器。感謝您提交這個,但。也許它會幫助別人。 –

+0

@Byron Sommardahl - 抱歉,因爲噪音... –

0

可能是這樣的:

public IValueConverter<T> Create<T>(T type) 
{ 
    return _container.Resolve(typeof(IValueConverter<>).MakeGenericType(type.GetType())); 
} 

雖然我還沒有測試過它。

+1

這可能工作!取決於如何解決工作,但我相信這樣它會得到正確的類型。但是,您應該傳遞'type'而不是'T'。我假設'T type'是爲了'Type type'。 –

+0

問題是,他在編譯時不知道'T',所以他不能爲某些具體的'T'調用'Create '(因爲他不知道它),編譯器無法推斷它從'Create(type)',因爲'type'是'Type'的一個實例。在編譯時不知道「T」的根本問題是,他不能有很強的輸入。 – jason

+0

嗯,我可能會錯過一些東西,但是使用反射調用Create會出現什麼問題呢? – driushkin

0

在完全不同的說明中,如果您只是在封面上調用Convert,爲什麼不使用System.Convert.ChangeType(Object, Type)

如果您需要添加對自定義類型的支持,您可以創建自己的類型轉換器並通過TypeConverterAttribute註冊。

1

麻煩的是,這時,我的.Create方法認爲我正在尋找一個ValueConverter<Type>,而不是像ValueConverter<int>一個更合適的值轉換器。

首先,你應該明白爲什麼會發生這種情況。你沒給我們調用的代碼,但它可能看起來是這樣的:

Type type = SomehowResolveTheTypeThatINeedToConvertTo(); 
factory.Create(type); 

就在那裏,那是要調用泛型方法

IValueConverter<T> ValueConverterFactory.Create<T>(T type) 

其中Type代替類型參數T

其次,你需要明白,你正在努力從根本上無法完成。在編譯時你不知道類型,因此你不能有強大的輸入。要找回強類型的IValueConverter<T>,您需要知道什麼是T。您需要願意接受您的轉換器返回object而不是T,或者找到一種方法,讓您知道編譯時的類型T

0

這通常是在driushkin躲避時完成的。您需要將您的開放式泛型與您的容器一起註冊。隨着StructureMap,這將是這樣的:

Scan(scanner => 
{ 
    scanner.TheCallingAssembly();  
    scanner.ConnectImplementationsToTypesClosing(typeof (IValueConverter<>)); 
}); 

在Autofac,這將是這樣的:

builder.RegisterGeneric(typeof(IValueConverter<>)); 

這樣,你會逐漸建立起自己的泛型類型來解決:

Type openType = typeof(IValueConverter<>); 
Type closedType = openType.MakeGenericType(type); 
var instance = container.Resolve(closedType); 

我不認爲你想你的工廠方法的參數是通用的,但。它只是一個簡單的類型:

public IValueConverter<T> Create<T>(Type type) 
{ 
    // ... 
} 

而不是創建所有這些,但爲什麼不只是使用Automapper?這裏是映射的類型,我一般創建:

public class DateTimeToDateMapping : IAutoMapperInitializer 
{ 
    public void Initialize(IConfiguration configuration) 
    { 
     configuration.CreateMap<DateTime, Date>().ConstructUsing(
      dateTime => new Date(dateTime.Year, dateTime.Month, dateTime.Day)); 
    } 
} 

下面是此映射可用於:

var date = _mappingEngine.Map<DateTime, Date>(DateTime.Today); 

我不知道,我用System.Convert多,但它不這似乎並不意味着透露,我認爲它是硬連線,只知道如何轉換某些東西。如果你使用Automapper,那麼你也可以很容易地測試你的映射。