我有一组值类型转换器,可将字符串转换为各自的类型。我有一个工厂负责在需要时根据类型创建这些转换器。我试图让工厂和转换器保持通用,但我遇到了一些问题。在工厂调用.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 :(得分: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
。< / p>
答案 1 :(得分:0)
我不知道这是不是你的要求,但我在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));
}
答案 2 :(得分:0)
可能是这样的:
public IValueConverter<T> Create<T>(T type)
{
return _container.Resolve(typeof(IValueConverter<>).MakeGenericType(type.GetType()));
}
虽然我没有测试过。
答案 3 :(得分:0)
完全不同的说明,如果您只是在封面下调用转换,为什么不使用System.Convert.ChangeType(Object, Type)
?
如果您需要添加对自定义类型的支持,您可以创建自己的类型转换器并通过TypeConverterAttribute
注册它们。
答案 4 :(得分:0)
这通常就像躲避肌肉一样。您需要在容器中注册开放的通用类型。使用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,那么您也可以轻松测试映射。