I am trying to come up with a method factory that looks inside config to get the full name of the type to instantiate and creates that object type dynamically.
Here is my Type and the Interface:
public interface IComponent<T>
{
IEnumerable<T> DataSource {get; set;}
void PerformTask(object executionContext);
}
namespace MyCompany.Components
{
public class ConcreteComponent1<T> : IComponent<T>
{
private IEnumerable<Contact> contactSource = null;
internal ConcreteComponent1() {}
public void PerformTask(object executionContext)
{
this.contactSource = GetSource(executionContext);
foreach(var result in this.contactSource)
{
result.Execute(executionContext);
}
}
public IEnumerable<T> DataSource
{
get { return this.contactSource as IEnumerable<T>; }
set { this.contactSource = (IContactSource)value; }
}
}
}
Factory, resides in the same assembly:
//Factory - Same assembly
public static class ComponentFactory<T>
{
public static IComponent<T> CreateComponent()
{
var assembly = Assembly.GetExecutingAssembly();
object o = assembly.CreateInstance("MyCompany.Components.ConcreteComponent1"); //o is null...
var objectHandle = Activator.CreateInstance(Assembly.GetAssembl(typeof(ComponentFactory<T>)).GetName().FullName, "MyCompany.Components.ConcreteComponent1"); //throws Could not load type from assembly exception.
return o as IComponent<T>;
}
}
So in first case the o is always null.
In the second case when using the Activator class, it throws Type could not be loaded from assembly "MyAssembly". No inner exception. What am I doing wrong?
First of all, actual name of your type is:
MyCompany.Components.ConcreteComponent1`1
It can't be instantiated because you have to specify type parameters:
public static class ComponentFactory<T>
{
public static IComponent<T> CreateComponent()
{
Type generic = Type.GetType("MyCompany.Components.ConcreteComponent1`1");
Type concrete = generic.MakeGenericType(typeof(T));
var objectHandle = Activator.CreateInstance(
concrete,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
null, //here can come ctor params
null);
return objectHandle as IComponent<T>;
}
}
this will work with internal constructor.
I'd say the actual name of your class ConcreteComponent1 is not "MyCompany.Components.ConcreteComponent1" because it includes a generic. Execute
Console.WriteLine(typeof(ConcreteComponent1<T>).FullName);
to see the string representation for your class created by C#.
But why do you define your ConcreteComponent1 class the way you do? Wouldn't it be better to use something like this:
public class ConcreteComponent1 : IComponent<Contact> {
internal ConcreteComponent1() {}
public void PerformTask(object executionContext)
{
this.contactSource = GetSource(executionContext);
foreach(var result in this.contactSource)
{
result.Execute(executionContext);
}
}
public IEnumerable<Contact> DataSource
{
get { return this.contactSource; }
set { this.contactSource = value; }
}
}
This way you can use the expected name you already used in your example and you can remove the extra private field your approach introduces. As your ConcreteComponent1 class doesn't really need any generic functionality this would be a better approach in my opinion.
Related
I have a generic function which is like below
private List<T> GetAll<T>()
{
var listOfTModels = // gets the list of T from the database.
return listOfTModels;
}
I need to pass the model ( T ) dynamically to this function based on a string which will be decided at runtime.
public void SomeFunction(string modelName)
{
// Call the above Get method with modelName parameter as the 'T'
var listOfModels = GetAll<something>(); //not sure what type this something should be "Type" or "string"
// Further Logic on listOfModels
}
How could this be done?
You will need to use reflection to get the method like so:
typeof(ClassWithGetAll).GetMethod("GetAll",
BindingFlags.Instance | BindingFlags.NonPublic);
This will return a method info which you will then use to create a generic method via MakeGenericMethod.
The only way to get a type from a string name AFAIK is with Type.GetType but you will need a AssemblyQualifiedName for that, so passing in a short/simplified name like string or int or anything like that will more than likely return null.
If you figure out how to either get the qualified name or how to search for the type, the last thing would be to invoke the MethodInfo returned from the MakeGenericMethod call, here is a example of how the code could look:
public void SomeFunction(string modelName)
{
// No idea what the class/struct in which the method "GetAll"
// is called, hence use this name
var instance = new ClassWithGetAll();
//Retrieves the info of "GetAll<T>"
MethodInfo method = typeof(ClassWithGetAll).GetMethod("GetAll",
BindingFlags.Instance | BindingFlags.NonPublic);
//Commented out as you will need to figure out how to get the
// assembly qualified name of the input model name, unless
// it is qualified.
//modelName = GetAssemblyQualifiedName(modelName);
Type modelType = Type.GetType(modelName);
//Creates a generic method: "GetAll<T>" => "GetAll<modelType>"
method = method.MakeGenericMethod(modelType);
//Invokes the newly created generic method in the specified
// instance with null parameters, which returns List<modelType>
object list = method.Invoke(instance, null);
}
I think inheritance would be the right way to solve your problem.
public class TBase
{
public int Prop { get; set; }
}
public class TChildOne : TBase
{
}
public class TChildTwo : TBase
{
}
public class GClass
{
private List<T> GetAll<T>() where T : TBase
{
var listOfTModels = // gets the list of T from the database.
return listOfTModels;
}
public void Main(string strType)
{
switch (strType)
{
case "TChildOne":
{
var child = GetAll<TChildOne>();
break;
}
case "TChildTwo":
{
var child = GetAll<TChildTwo>();
break;
}
default:
throw new Exception("type not found");
}
}
}
I have existing test code which needs to be extended.
I would like to keep this as generic as possible in order to reduce code duplication, here is a simplified snippet of the current code:
public class FilterValueOne { }
[TestClass]
public class TestClass
{
[TestMethod]
public void FilterValueOne_TestMethod()
{
ManipulateData(BuildFilter());
}
private void ManipulateData(FilterValueOne filter)
{
// Do some stuff with filter
}
private FilterValueOne BuildFilter()
{
var filter = new FilterValueOne();
// Initialize filter data members here...
return filter;
}
}
This works, but it is limited to the "FilterValueOne" type.
I want to expand this and make it more generic by implementing a generic type argument.
The code snippet below is what I am after:
// Enum will expand as the app grows
public enum TypeEnum
{
ValueOne,
ValueTwo,
ValueThree
}
// More classes will be added as the app grows
public class FilterValueOne { }
public class FilterValueTwo { }
public class FilterValueThree { }
[TestClass]
public class TestClassGeneric
{
// _filter to be used as the generic type argument
private object _filter;
// Constructor
public TestClassGeneric(TypeEnum val)
{
switch (val)
{
case TypeEnum.ValueOne:
_filter = GetTestObject<FilterValueOne>();
break;
case TypeEnum.ValueTwo:
_filter = GetTestObject<FilterValueTwo>();
break;
case TypeEnum.ValueThree:
_filter = GetTestObject<FilterValueThree>();
break;
default:
_filter = null;
break;
}
}
private T GetTestObject<T>()
where T : class
{
// Code simplified
object returnValue = new object();
return (T)returnValue;
}
[TestMethod]
public void Generic_FilterValue_TestMethod()
{
// Error: The type _filter could not be found. (However it exists)
ManipulateData(BuildFilter<_filter>());
}
private void ManipulateData<T>(T filter)
{
// Do some stuff with filter
}
private T BuildFilter<T>()
where T : class
{
// I want filter to be dynamically assigned to whatever _filter was
// assigned to in the constructor (FilterValueOne/Two/Three), instead
// of "FilterValueOne"
var filter = typeof(T);
// Initialize filter data members here...
return filter;
}
}
I ultimately want to use "_filter" as a generic type argument in my test method, but I can't get it to work. I receive an error stating that "_filter could not be found".
I have tried multiple ways of using typeof(_filter) etc. but no success.
I don't understand generics and its potential fully, I don't know if this is even possible or if I'm simply missing something.
The T in BuildFilter<T> is a type parameter. You are passing an instance of a type, in your case an instance of object (aka System.Object) not a type.
You simply cannot do that, hence the compiler error (_filter is not a type; it's an instance and that's why the compiler can't find that "type").
What you want to do instead is instantiate a specific BuildFilter<FilterValueOne>, etc. and create tests that test each of these things.
For example
[TestMethod]
public void Generic_FilterValueOne_TestMethod()
{
ManipulateData(new BuildFilter<FilterValueOne>());
}
It looks like you're using MS Test; AFAIK MS Test does not support generic tests, so you will have to create a test for each type you're interested in.
It's easier to use generic test class, but if you prefer to pass filter type through contructor or as a method parameter you can use Activator class.
public class TestClassGeneric<T> where T : new()
{
public void Generic_FilterValue_TestMethod()
{
var filter = new T();
// ManipulateData(filter);
}
}
public class TestClassConstructorArg
{
private readonly Type type;
public TestClassConstructorArg(Type type)
{
this.type = type;
}
public void Generic_FilterValue_TestMethod()
{
var filter = Activator.CreateInstance(type);
// var filter = Activator.CreateInstance(type, BindingFlags.CreateInstance, arguments...);
// ManipulateData(filter);
}
}
I am trying to create a Generic interface where the parameter type of one of the methods is defined by the generic
EDIT
I've changed the question slightly after realising I have probably confused matters by specifying a type parameter in the Factory creation method. What I have is two types of API calls that I need to make to a 3rd party API. The first retrieves a record from the API using an Id that is an int. The second also retrieves a record from the API but the Id is a string (guid). I have a class for each record type (ClientEntity and InvoiceEntity) that both implement a Generic Interface where I pass in the Id type
This is the Interface in which I declare a Method with an id Parameter
public interface IGeneric<TId>
{
void ProcessEntity(TId id);
}
I implement the interface in a couple of classes, one sets the id to be an int, the other a string.
public class ClientEntity: IGeneric<int> // Record with Id that is an int
{
public void ProcessEntity(int id)
{
Console.WriteLine(id);
// call 3rd party API with int Id
}
}
public class InvoiceEntity: IGeneric<string> // Record with Id that is a string (guid)
{
public void ProcessEntity(string id)
{
Console.WriteLine(id);
// call 3rd party API with string Id
}
}
What I would like to know is how do I use this within a factory pattern?
public static class GenericFactory
{
public static IGeneric<WhatGoesHere> CreateGeneric(string recordType)
{
if (recordType == "Client")
{
return new ClientEntity();
}
if (type == "Invoice")
{
return new InvoiceEntity();
}
return null;
}
}
The objective is to use the factory to instantiate the correct class so that I can call the ProcessEntity method
EDIT
I don't want to have to pass in the Generic type to the factory method because the class that is created by the factory should handle that. When I create the object, I don't know what Id type is required, I want the factory to handle that
e.g.
var myGeneric = GenericFactory.CreateGeneric("Client");
myGeneric.ProcessEntity("guid")
or
var myGeneric = GenericFactory.CreateGeneric("Invoice");
myGeneric.ProcessEntity(1234)
I hope that makes sense
You should be able to do something like this:
public static class GenericFactory
{
public static IGeneric<T> CreateGeneric<T>()
{
if (typeof(T) == typeof(string))
{
return (IGeneric<T>) new GenericString();
}
if (typeof(T) == typeof(int))
{
return (IGeneric<T>) new GenericInt();
}
throw new InvalidOperationException();
}
}
You would use it like this:
var a = GenericFactory.CreateGeneric<string>();
var b = GenericFactory.CreateGeneric<int>();
Note that this uses a strongly-typed call rather than passing in the type name as a string (which may or may not be what you actually want).
If instead you want to pass a string for the type name, you will have to return an object because there is no way to return the actual type:
public static object CreateGeneric(string type)
{
switch (type)
{
case "string": return new GenericString();
case "int": return new GenericInt();
default: throw new InvalidOperationException("Invalid type specified.");
}
}
Obviously if you have an object you would normally have to cast it to the right type in order to use it (which requires that you know the actual type).
Alternatively, you could use reflection to determine what methods it contains, and call them that way. But then you'd still need to know the type in order to pass a parameter of the right type.
I think that what you are attempting to do here is not the right approach, which you will discover once you start trying to use it.
Hacky solution: Use dynamic
Nevertheless, there is one way you can get something close to what you want: Use dynamic as follows (assuming that you are using the object CreateGeneric(string type) factory method from above):
dynamic a = GenericFactory.CreateGeneric("string");
dynamic b = GenericFactory.CreateGeneric("int");
a.ProcessEntity("A string");
b.ProcessEntity(12345);
Be aware that dynamic uses reflection and code generation behind the scenes, which can make the initial calls relatively slow.
Also be aware that if you pass the wrong type to a method accessed via dynamic, you'll get a nasty runtime exception:
dynamic a = GenericFactory.CreateGeneric("string");
a.ProcessEntity(12345); // Wrong parameter type!
If you run that code, you get this kind of runtime exception:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'ConsoleApplication1.GenericString.ProcessEntity(string)' has some invalid arguments
at CallSite.Target(Closure , CallSite , Object , Int32 )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
at ConsoleApplication1.Program.Main() in D:\Test\CS6\ConsoleApplication1\Program.cs:line 71
Usually for that Factory using some DI container (DI can be useful, for example, when GenericInt or GenericString has dependencies), but to demonstrate just Idea how you can resolve this:
void Main()
{
GenericFactory.CreateGeneric<int>();
GenericFactory.CreateGeneric<string>();
}
public static class GenericFactory
{
private static Dictionary<Type, Type> registeredTypes = new Dictionary<System.Type, System.Type>();
static GenericFactory()
{
registeredTypes.Add(typeof(int), typeof(GenericInt));
registeredTypes.Add(typeof(string), typeof(GenericString));
}
public static IGeneric<T> CreateGeneric<T>()
{
var t = typeof(T);
if (registeredTypes.ContainsKey(t) == false) throw new NotSupportedException();
var typeToCreate = registeredTypes[t];
return Activator.CreateInstance(typeToCreate, true) as IGeneric<T>;
}
}
public interface IGeneric<TId>
{
TId Id { get; set; }
void ProcessEntity(TId id);
}
public class GenericInt : IGeneric<int>
{
public int Id { get; set; }
public void ProcessEntity(int id)
{
Console.WriteLine(id);
}
}
public class GenericString : IGeneric<string>
{
public string Id { get; set; }
public void ProcessEntity(string id)
{
Console.WriteLine(id);
}
}
The answer marked correct is fine if you want to use Static class but but what if you
want to return an DI injected type instead of newing an object? I suggest the
following!
public interface IGenericFactory
{
IGeneric<T> GetGeneric<T>() where T : class;
}
public class GenericFactory: IGenericFactory
{
private readonly IGeneric<int> intGeneric;
private readonly IGeneric<string> stringGeneric;
public GenericFactory(IGeneric<int> intG, IGeneric<string> stringG)
{
intGeneric = intG;
stringG = stringG;
}
public IGeneric<T> GetGeneric<T>() where T : class
{
if (typeof(T) == typeof(IGeneric<int>))
return (IGeneric<T>)Convert.ChangeType(intGeneric, typeof(IGeneric<T>));
if (typeof(T) == typeof(IGeneric<string>))
return (IGeneric<T>)Convert.ChangeType(stringGeneric,typeof(IGeneric<T>));
else
throw new NotSupportedException();
}
}
Please note i simply injected the two expected return types for clarity in the constructor. I could have implemented the factory as a Dictionary and injected the return objects into this Dictionary. Hope it helps.
I'm thinking you don't want to have to enter the type parameter similar to the LINQ methods. However the magic behind that happens because the type parameter is used in the normal parameter definitions. For example in the ToList<string>() method you can see that TSource is used between the parenthesis.
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source);
That's how the compiler knows that you want a List<string> if you call ToList() instead of ToList<string>() when called from an IEnumerable<string>
However, I don't think you need a generic type parameter in your factory method at all. All you have to do is create a non-generic version of your TGeneric<TId>
public interface IGeneric { }
public interface IGeneric<TId> : IGeneric
{
void ProcessEntity(TId id);
}
And remove the <WhatGoesHere> from the CreateGeneric method:
public static IGeneric CreateGeneric(string recordType)
{
if (recordType == "Client")
{
return new ClientEntity();
}
if (recordType == "Invoice")
{
return new InvoiceEntity();
}
return null;
}
If the function does not know the type, make it generic.
If the children are generics of different types (<int>, <string>), return object and cast inside the same factory class (Factory<T>), It is safe by typeof.
Personally, I prefer to specify the type with generics, without using an additional parameter, eg a string.
public class Program
{
public static void Main(string[] args)
{
List<Number> something = new();
Do(something);
}
public static void Do<T>(List<T> list)
{
list.Add(Factory<T>.Create());
}
}
public abstract class Factory<T>
{
private static Object ConcreteF()
{
if (typeof(T) == typeof(Number))
return new ChildGenericNumber();
throw new Exception("");
}
public static T Create()
{
return (Factory<T>)ConcreteF()).Build();
}
protected abstract T Build();
}
How can I use reflection to pass each list of "MyTypes" to a generic method with a constraint of T:MyDataObject?
public interface IAllMyTypes
{
List<FirstType> MyType1 { get; set; }
List<SecondType> MyType2 { get; set; }
List<ThirdType> MyType3 { get; set; }
}
FirstType, SecondType, and ThirdType inherit from MyDataObject (as demonstrated below), but have different properties.
public class FirstType : MyDataObject
{
//various properties
}
I've been unable to pass the data into a method with this signature:
void DoSomething<T>(IEnumerable<T> enumerable) where T : MyDataObject;
The error is that "type arguments can not be inferred."
Here is my unsuccessful attempt:
public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
{
var x = propertyInfo.GetValue(allMyTypes) as IList;//im not sure what to do here
if(x==null) throw new Exception("still wrong");
DoSomething(x);
}
}
All of the code in DoSomething(..) works correctly if I provide the properties directly which looks like:
public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
DoSomething(allMyTypes.MyType1);
DoSomething(allMyTypes.MyType2);
DoSomething(allMyTypes.MyType3);
}
If you want to use reflection, you can invoke your helper method using reflection, too:
You will have to obtain a MethodInfo to a generic method and create a generic method reflection handle to actually Invoke the method. The type T of the generic method needs to be obtained at runtime in that case.
public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
var method = this.GetType().GetMethod("DoSomething", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
{
var x = propertyInfo.GetValue(allMyTypes, null);
if(x==null) throw new Exception("still wrong");
// obtain the type from the property - other techniques can be used here.
var genericMethod = method.MakeGenericMethod(new[] {propertyInfo.PropertyType.GetGenericArguments()[0]})
//execute the generic helper
genericMethod.Invoke(this, new[]{x});
}
}
public void DoSomething<T>(IList<T> list) where T : MyDataObject {
}
I'm struggling to find a case where you'd need to structure your data the way you did without over-complicating things. If you've found a legit case please comment and I'll update my answer to reflect your needs.
You can start with your base class, make it abstract and put an abstract method in it DoSomething
public abstract class MyDataObject{
public string SomeSharedProperty{get;set;}
protected abstract DoSomething();
}
public class FirstType: MyDataObject{
protected override DoSomething(){
Console.WriteLine(SomeSharedProperty);
}
}
public class Consumer{
public void DoSomethingWithAllMyTypes(List<MyDataObject> source)
{
source.ForEach(x=>x.DoSomething());
}
}
You could use the Linq method call Cast<T> to convert your list to the right type
public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
{
var x = propertyInfo.GetValue(allMyTypes) as IEnumerable
if(x==null) throw new Exception("still wrong");
DoSomething(x.Cast<MyDataObject>());
}
}
I have the following test class:
public class OutsideClass
{
private List<Type> _interfaces = null;
public void InjectInterfaces(Type[] types)
{
if(_interfaces == null)
{
_interfaces = new List<Type>();
}
foreach (var type in types)
{
if(type.IsInterface)
{
_interfaces.Add(type);
}
}
}
public void PerformSomethingWithTheInterfaces()
{
foreach (var i in _interfaces)
{
new Test<i>().PerformSomething(); // On this line the error occurs
}
}
}
internal class Test<T>
{
internal void PerformSomething()
{
}
}
This gives me on however the message Type or namespace name expected. How can I adjust this code so that it works?
What I am trying to do is to pass in a bunch of interfaces to a class library, there loop over the interfaces and use Unity to Resolve, based on the interface, something. I use the Resolve extension method.
You'd need to use reflection... something like this:
foreach (Type type in _interfaces)
{
Type concreteType = typeof(Test<>).MakeGenericType(new Type[] { type });
MethodInfo method = concreteType.GetMethod("PerformSomething",
BindingFlags.Instance | BindingFlags.NonPublic);
object instance = Activator.CreateInstance(concreteType);
method.Invoke(instance, null);
}
(You may need to make minor changes - the above isn't tested or even compiled.)
With C# 4 and dynamic typing, you can make it somewhat simpler:
foreach (Type type in _interfaces)
{
Type concreteType = typeof(Test<>).MakeGenericType(new Type[] { type });
dynamic d = Activator.CreateInstance(concreteType);
d.PerformSomething();
}
You cannot pass values as generic arguments. Only types. To be clear:
typeof(string) != string.
I don't think this can work. You specialize a generic statically on the type name. You can't pass in a reference to the Type object.
There are ways to do what you want, but they involve C# 4 and DynamicObject.
You might really want to look at C# 4's MEF just as an idea as a unity replacement I think it stands up really well myself and simplifies the resolution mechanisms a lot, and may give functionality to complete the task you're attempting more simply..
namespace MEF_Interface
{
// Interface to recognize the concrete implementation as
public interface IMessageWriter
{
void WriteMessage();
}
}
namespace MEF_HelloMessageWriter
{
// Concrete implementation in another assembly
[Export(typeof(IMessageWriter))]
public class HelloMessageWriter : IMessageWriter
{
public void WriteMessage() { Console.WriteLine("Hello!"); }
}
}
namespace MEF_GoodbyeMessageWriter
{
// Concrete implementation in another assembly
[Export(typeof(IMessageWriter))]
public class GoodbyeMessageWriter : IMessageWriter
{
public void WriteMessage() { Console.WriteLine("Goodbye!"); }
}
}
namespace MEF_Example
{
class DIContainer
{
[Import]
public IMessageWriter MessageWriter { get; set; }
public DIContainer(string directory)
{
// No more messy XML DI definition, just a catalog that loads
// all exports in a specified directory. Filtering is also available.
DirectoryCatalog catalog = new DirectoryCatalog(directory);
catalog.Refresh();
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
class Program
{
static void Main(string[] args)
{
string helloMessageWriterPath =
#"C:\shared\Projects\MEF_Example\MEF_HelloMessageWriter\bin\Debug";
string goodbyeMessageWriterPath =
#"C:\shared\Projects\MEF_Example\MEF_GoodbyeMessageWriter\bin\Debug";
DIContainer diHelloContainer = new DIContainer(helloMessageWriterPath);
diHelloContainer.MessageWriter.WriteMessage();
DIContainer diGoodbyeContainer = new DIContainer(goodbyeMessageWriterPath);
diGoodbyeContainer.MessageWriter.WriteMessage();
Console.ReadLine();
}
}
}