[英]Is it possible to instantiate a generic type from a factory method that takes a string?
我正在编写一个由导入程序和处理器组成的应用程序。 导入器将采用一种类型并实例化该类型的List<>
。 处理器需要获取返回的列表。
例如,我想调用Importer<Organisation>.GetObjects()
并让它返回List<Organisation>
。 然后将列表传递给我的OrganisationProcessor。
同样,我想调用Importer<Address>.GetObjects()
并让它返回List<Address>
。 然后将列表传递给我的AddressProcessor。
我想将Importer<>
和* Processor的创建放入工厂。 我删除了所有代码,并在下面生成了一个未被污染的示例。 我的尝试没有成功,可能归结为我不完全理解的方方面面。
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 { }
这应该可以解决问题,请注意您的最初意图没有改变,即使是我也很惊讶。 还要注意,您必须添加名称空间以在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 { }
GetImporter
和GetProcessor
的返回类型必须为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);
}
请注意,传入的字符串名称必须是包括名称空间在内的类型的全名,并且仅限于与那些方法位于同一程序集中的类型(组织,地址)。 如果要在其他程序集中使用类型,则必须使用程序集限定名称 ,但是用于生成处理器类型名称的字符串串联将不起作用。
我可能会在处理器类型上使用自定义属性,然后在具有自定义属性的程序集中找到所有类型,而不是通过简单的字符串串联来生成上面的类型名称。 上面的方案很脆弱,如果重命名某些类型,很容易破坏。
是的,有可能。 假设您的类在同一程序集中,则下面的代码没有进行很多优化,但可以解决问题:
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);
}
}
因此,您可以像下面这样使用上方的Factory:
dynamic importer = Factory.GetImporter(name);
dynamic processor = Factory.GetProcessor(name);
processor.Process(importer.GetObjects());
在工厂内,Get ...方法可以使用Activator.CreateInstance方法 。 该方法可以通过具有附加参数的assamby-name和type-name创建类的实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.