[英]Generic factory that creates generic classes
我有一組值類型轉換器,可以將字符串轉換為各自的類型。 我有一個工廠負責在需要時根據類型創建這些轉換器。 我試圖讓工廠和轉換器保持通用,但我遇到了一些問題。 在工廠調用.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);
問題是,然后,我的
.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
我不知道這是不是你的要求,但我在Rhino Igloo框架中找到了這個優雅而簡短的代碼: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));
}
也許是這樣的:
public IValueConverter<T> Create<T>(T type)
{
return _container.Resolve(typeof(IValueConverter<>).MakeGenericType(type.GetType()));
}
雖然我還沒有測試過。
在完全不同的說明中,如果你只是在封面下調用Convert,為什么不使用System.Convert.ChangeType(Object, Type)
?
如果需要添加對自定義類型的支持,可以創建自己的類型轉換器並通過TypeConverterAttribute
注冊它們。
這通常就像滴露出來一樣。 您需要在容器中注冊開放的通用類型。 使用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,那么您也可以輕松地測試您的映射。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.