簡體   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