繁体   English   中英

创建泛型类的通用工厂

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM