是否可以从采用字符串的工厂方法实例化泛型类型?

时间:2012-08-22 07:32:11

标签: c# generics factory instantiation

我正在编写一个由Importer和Processor组成的应用程序。导入器将采用一种类型并实例化该类型的List<>。处理器需要获取返回的列表。

例如,我想致电Importer<Organisation>.GetObjects()并让它返回List<Organisation>。然后将List传递给我的OrganisationProcessor。

同样,我想致电Importer<Address>.GetObjects()并让它返回List<Address>。然后将List传递给我的AddressProcessor。

我想将Importer<>和*处理器的创建放入工厂。我删除了所有代码并在下面生成了未受污染的示例。我的尝试没有成功,也可能是我不完全理解的差异等主题。

class Program
{
    static void Main(string[] args)
    {
        //What I am currently doing
        Importer<Organisation> importer = new Importer<Organisation>();
        List<Organisation> list = importer.GetObjects();
        OrganisationProcessor processor = new OrganisationProcessor();
        processor.Process(list);


        //***What I want to do*** 
        string name = "Organisation";  //Passed in from external source
        var importer = Factory.GetImporter(name);  //returns Importer<Organisation>
        var processor = Factory.GetProcessor(name); //returns OrganisationProcessor
        processor.Process(importer.GetObjects()); //.Process takes List<Organisation>
    }
}

public class Importer<T>
{
    public List<T> GetObjects()
    {
        return new List<T>();
    }
}


public class OrganisationProcessor
{
    public void Process(List<Organisation> objects)
    {
        //Do something
    }
}

public class AddressProcessor
{
    public void Process(List<Address> objects)
    {
        //Do something
    }
}

public class Organisation { }

public class Address { }

4 个答案:

答案 0 :(得分:2)

这应该可以解决问题,请注意你的初始意图没有改变,即使对我来说也是如此。另请注意,您必须在Factory中添加名称空间以输入文字。使用LINQ查找类型更加灵活,但输入路径以在一行中使用命名空间进行输入更具可读性。我已经在没有命名空间的项目上执行了这个示例,它运行正常。

void Main()
{
    string name = "Organisation";  //Passed in from external source
    var importer = Factory.GetImporter(name);  //returns Importer<Organisation>
    var processor = Factory.GetProcessor(name); //returns OrganisationProcessor
    processor.Process(importer.GetObjects()); //.Process takes List<Organisation>
    Console.WriteLine (importer.GetObjects().GetType());
}

public static class Factory
{
    public static Importer GetImporter(string name)
    {           
            //add namespace to name here
        var desiredType = Type.GetType(name);

        return new Importer(desiredType);
    }
    public static Processor GetProcessor(string name)
    {
            //add namespace to name here
        var desiredType = Type.GetType(name+"Processor");

        return (Processor)Activator.CreateInstance(desiredType);
    }
}

public class Importer 
{
    Type _typeOfEntities;

    public Importer(Type typeOfEntities)
    {
        _typeOfEntities = typeOfEntities;
    }
    public IList GetObjects()
    {
        Type generic = typeof(List<>);

        Type constructed = generic.MakeGenericType(new Type[] {_typeOfEntities});

        return (IList)Activator.CreateInstance(constructed);
    }
}
public interface Processor
{
    void Process(IList objects);
}

public class OrganisationProcessor : Processor
{
    public void Process(IList objects)
    {
        var desiredTypedObject = objects as List<Organisation>;
        if(desiredTypedObject != null)
        ProcessImp(desiredTypedObject);
    }
    private void ProcessImp(List<Organisation> objects)
    {
        Console.WriteLine ("processing Organisation");
    }
}

public class AddressProcessor
{
    public void Process(List<Address> objects)
    {
        //Do something by analogy
    }
}

public class Organisation { }

public class Address { }

答案 1 :(得分:1)

GetImporterGetProcessor的返回类型必须为object。没有办法让这些方法在字符串中使用运行时信息,并返回在编译时类型已知的对象。

您的样本必须看起来像这样:

string name = "Organisation";  //Passed in from external source
dynamic importer = Factory.GetImporter(name);  //returns Importer<Organisation>
dynamic processor = Factory.GetProcessor(name); //returns OrganisationProcessor
processor.Process(importer.GetObjects()); //.Process takes List<Organisation>

其次,GetImporter方法类似于

public static object GetImporter(string name)
{
    Type type = Type.GetType(name);
    Type importerType = typeof(Importer<>).MakeGenericType(type);
    return Activator.CreateInstance(importerType);
}

最后,GetProcessor将类似于

public static object GetProcessor(string name)
{
    Type type = Type.GetType(name + "Processor");
    return Activator.CreateInstance(type);
}

请注意,传入的字符串名称必须是包含命名空间的类型的全名,并且这仅限于与这些方法位于同一程序集中的类型(组织,地址)。如果要在其他程序集中使用类型,则必须使用assembly qualified name,但是然后该字符串连接以生成处理器类型的名称将不起作用。

我可能会在处理器类型上使用自定义属性,然后使用自定义属性而不是简单的字符串连接来查找程序集中的所有类型,以生成上面的类型名称。上述方案很脆弱,如果重命名某些类型,可能很容易破解。

答案 2 :(得分:1)

是的,这是可能的。假设您的类在同一个程序集中,下面的代码没有太多优化但会解决问题:

public static class Factory
{
    public static object GetImporter(string name)
    {
        var assembly = Assembly.GetExecutingAssembly();

        var type = assembly.GetTypes().Single(t => t.Name.Equals(name));
        var importType = typeof (Importer<>).MakeGenericType(type);

        return Activator.CreateInstance(importType);

    }

    public static object GetProcessor(string name)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var type = assembly.GetTypes()
               .Single(t => t.Name.Equals(string.Format("{0}Processor", name)));

        return Activator.CreateInstance(type);
    }
}

所以,你使用上面的工厂如下:

dynamic importer = Factory.GetImporter(name);
dynamic processor = Factory.GetProcessor(name);
processor.Process(importer.GetObjects());

答案 3 :(得分:0)

在您的工厂内,Get ... Methodes可以使用Activator.CreateInstance Method。此方法可以通过assamby-name和带有其他参数的type-name创建类的实例。