创建泛型类的通用工厂

时间:2011-03-07 18:52:19

标签: c# generics factory

我有一组值类型转换器,可将字符串转换为各自的类型。我有一个工厂负责在需要时根据类型创建这些转换器。我试图让工厂和转换器保持通用,但我遇到了一些问题。在工厂调用.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);

5 个答案:

答案 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,那么您也可以轻松测试映射。