I need to keep a list of created objects in my application. I have an abstract object and a number of derived classes. I would like to keep a list of created objects in an attempt to not needlessly create new objects.. im trying to do this with the below code, where T is derived from AbstractMapper. but getting the error
Cannot convert type 'AbstractMapper' to 'T'
when adding it to the list
protected List<AbstractMapper> Mappers = new List<AbstractMapper>()
public AbstractMapper Mapper<T>()
{
foreach (var mapper in Mappers)
{
if (mapper.Type == typeof (T).Name)
{
return mapper;
}
}
var newMapper = GetClass<T>("mapper");
Mappers.Add((AbstractMapper)newMapper);
return (AbstractMapper)newMapper;
}
You seem to lack the generic constraint to help the compiler make sure your code is type safe
public AbstractMapper Mapper<T>()
where T : AbstractMapper
This way you constraint the usage to only these Ts that inherit from AbstractMapper.
Anyway, the compiler should warn you that your T is not convertible to AbstractMapper, not the other way around.
Are you sure you're not seeing the following error?
Cannot convert type 'T' to 'AbstractMapper'
The problem is that the compiler cannot guarantee that your generic type parameter T is a subtype of AbstractMapper. You should add a generic type constraint:
public AbstractMapper Mapper<T>() where T : AbstractMapper
Then you could consider returning T instead of AbstractMapper.
You might also consider using a Dictionary instead of a List, where the key is typeof(T). If you want an object pool of derived types, you can also use a static field of a generic type:
public static class MapperProvider<T> where T : AbstractMapper
{
public static T Instance = GetType<T>(); //static initialization
}
Each generic type created from the generic type definition MapperProvider<T> will have a different static Instance field, and looking up the appropriate instance from Mapper<T> is then as simple as returning MapperProvider<T>.Instance.
Related
I'd like to instantiate instances of a generic type and pass them around as if they were generic objects of an interface that they implement, such as below. Clearly, this is not allowed. Why is this, and what is the general practice for handling such situations? thanks.
public class MyType<T> where T : IComparable { }
MyType<IComparable> things = new MyType<Int32>();
this gets error:
Cannot implicitly convert type MyType<Int32> to MyType<IComparable>
I want to do this because I need different types of things that I want to pass around to more generic methods such as
public void DoSomething(MyType<IComparable> things) {...}
The assignment compatibility of generic type arguments does not make the generic type itself assignment compatible. This is why: Let's assume that we declared the generic class like this:
public class MyType<T> where T : IComparable
{
public T Value { get; set; }
}
And let's assume that this would compile ...
var intObject = new MyType<int> { Value = 42 };
MyType<IComparable> things = intObject; // Does not compile!
... then we could write
// things.Value has the static type IComparable
things.Value = "hello"; // Allowed because string is IComparable
But this is not possible since the underlying object is a MyType<int> and thus its Value property has the type int. Therefore, we are not allowed to substitute a MyType<int> for a MyType<IComparable>.
I have the following code which works but I think it may not beoptimal because in theory compiler can determinate a generic type from the calling child class. Is there a way to rewrite this code such that I do not need to provide the ChildType generic type parameter?
public abstract class Test<ChildType, T> where ChildType: Test<ChildType, T>, new()
{
public T Field { get; set; }
public static ChildType Get(T field) {
return new ChildType() { Field = field };
}
}
public class ChildTest: Test<ChildTest, string>
{
}
// Call sample
var child = ChildTest.Get("test");
The answer is that it is pretty much required for the Test class to have the ChildType generic type specified. You could use reflection to get the current type and instantiate an instance of it but reflection is generally best avoided.
To see why consider the Test class on its own and in particular this line:
return new ChildType() { Field = field };
That line requires two pieces of information. First it needs to know what the actual type of ChildType is. You can't hardcode it because it can of course vary. The second piece of information it needs is to know it can create a new object of this type in this way.
Both of these pieces of information are provided through the generic type parameter and the generic constraint which is why it is required.
I have two generic functions. In the first one I fill a dictionary and then call the next generic function to convert the dictionary to object.
Here I need to return to the T generic object rather than specifying to a particular one. I am not able to achieve this. It shows error:
The type T must be a reference type in order to use it as a parameter "T" in the generic method or type..
public T Fill<T>()
{
Dictionary<string,string> d = new Dictionary<string,string>();
//filled dictionary -----
SomeClass dObject = ToObject<SomeClass>(d);
//[---Here I need to return a dynamic object rather than fixing to SomeClass--]
T dObject = ToObject<T>d); ///* Not able to acheive this *///
return (T)Convert.ChangeType(dObject, typeof(T));
}
private T ToObject<T>(IDictionary<string, string> dict)
where T : class,new()
{
T t = new T();
PropertyInfo[] properties = t.GetType().GetProperties();
//--- code to convert object to dictionary
return t;
}
Because the ToObject method is constrained with class and new(), you also need to match that to your Fill method.
public T Fill<T>() where class, new()
However, there doesn't appear to be a need for the class constraint so you can remove it if you like.
In this line of code you have a constraint that T must be a class and it must have a parameterless constructor (new):
private T ToObject<T>(IDictionary<string, string> dict)
where T : class,new()
Your Fill method's signature is like this:
public T Fill<T>()
See there are no constraints which means I can pass a struct or any other type to it, even a type which does not have a parameterless constructor. This is why you are getting the error. You need to define the same constraint or something more specific than the constraint you have defined on ToObject<T> method. To fix your issue, simply do this:
public T Fill<T>() where class, new()
Now Fill<T> has the same constraint so I can only call it with a class who has a parameterless constructor.
I want to pass in dynamically a class name and a method name to a method and keep this dynamic, I'm understanding that I should use generics and possible constraints.
Example, I have a class
MemberRequestDTO (contains several properties)
I also that a Method called
RecordsToRetrieve
Using some reflection I was wanting to dynamically get the values of the properties, which I figured out how to do that, but then I realized that is is too hard code and tightly coupled of which I figured time to refactor and create a method with a signature that uses generics with constraints. having trouble with understand the use of and the constraints etc..
So I want to pass in a class name and be able to use it in the method, with reflection I plan to use it like:
Type type = typeof(classname);
I started reading and researching and I start playing with code like this:
public void GetTypeValues<T>() where T : class , new()
How do I pass in the class name of MemberRequestDTO?
What does the Generic new for me?
How do I pass a class name into the parens ()?
If I use does it also get pass into parens?
How can I pass in class and method?
Reading the above "Where T has the constraints (enforced) to be of type "class AND new() ?
A little lost and confused on this, forgive me.
EDIT:
Based on the answers and some research, I'm understanding this a bit more:
Lets forget about me trying to pass in a method, say I just want to pass in a class
Say the class with properties looks like this
public class MemberRequestDTO
{
public DateTime DateRequested { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then I will New this up
var memberRequestDTO = new MemberRequestDTO();
Then I want to pass this to class into a method that is generic
How do I go about passing an instance of a object into a generic method? What about the signature , example public void GetTypeValues() where T : class , new()
Would I want to have the contraints of class and new() ?
For the above, is T the instance of the class? Thus the purpose is that I can be
Saying
GetTypeValues(memberRequestDTO)
( this is my actual question , pass into whatever class I instantiated and that let the method "handle" dealing with that class with looping through the properties and getting me the name values of the properties dynamically and yes it probably will not remain a void method )
Should passing in memberRequestDTO be with quotes or without? I want to be able to pass in any instance of a class to the member to then manipulate it more. () should T be there ? should the parens () be empty or contain an generic parameter for the class object ?
Here are your answers:
GetTypeValues<MemberRequestDTO>()
new() is a constraint for the Type Parameter - T. It says that the type argument T must have a public parameterless constructor. In your case, MemberRequestDTO class must a public parameterless constructor like below:
public class MemberRequestDTO
{
public MemberRequestDTO() { ... }
}
As a class name is of reference type, you can pass it as a type into the parens like: SomeMethod(typeof(MemberRequestDTO)); where the signature of the method be void SomeMethod(Type type) { }
If you pass the class as a type parameter as in point (1), it does not get passed into the parens()
class constraint implies that "The type argument must be a reference type; this applies also to any class, interface, delegate, or array type."
and new() constraint implies that "The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last."
EDIT:
If I catch your point, then the generic method definition would be something like:
public void GetTypeValues<T>(T typeObject) where T : class
{
// typeObject specific operations
}
That uses typeObject dynamically, getting the "execution-time compiler" to perform type inference to work out T. See the reference here. Moreover, imho, you don't need the new () constraint on T here.
After that, you can pass an instance of any class to this method like below:
var memberRequestDTO = new MemberRequestDTO();
GetTypeValues((dynamic) memberRequestDTO);
EDIT 2:
USAGE: Get Type Values dynamically using Reflection
This method returns the property values wrapping into IEnumerable<KeyValuePair<string, object>>.
public static IEnumerable<KeyValuePair<string, object>> GetTypeValues<T>(T typeObject) where T : class
{
// typeObject specific operations
IEnumerable<KeyValuePair<string, object>> typeValues =
typeObject
.GetType()
.GetProperties()
.Select(property => new KeyValuePair<string, object>(property.Name, property.GetValue(typeObject)));
return typeValues;
}
How do I pass in the class name of MemberRequestDTO ?
You already have one. In a generic method "Type parameter" in this case T will be the name of type you're interested in.
public void GetTypeValues<T>() where T : class , new()
{
string typeName = typeof(T).Name;
}
What does the Generic new for me?
It is a contraint which will prevent you to pass any type without public parameterless constructor. In other words it will allow you to new up type passed in as "Type parameter"
public void GetTypeValues<T>() where T : class , new()
{
T instance = new T();//This is not possible without new constraint
}
How do I pass a class name into the parens () ?
If I use does it also get pass into parens?
Not sure what is that parens() Need more info to answer this.
How can I pass in class and method?
If I understand correctly "Type parameter" T is the runtime type which you use. So you get a Type there. Am not sure about what you mean by class? Class cannot be passed only instances can be passed.
For methods there are number of ways. You can pass MethodInfo or method name or A delegate, or a MethodCallExpression etc.
Reading the above "Where T has the constraints (enforced) to be of
type "class AND new() ?
Yes. class constraint prevents you from passing value types, new() constraint allows you to new up things.
Read more about generics here and here
I'm a little confused about what you want to do but I'll give it a shot. I can see two possible interpretations and they differ on what the caller is starting with and what you're trying to achieve.
Interpretation #1: The caller starts out knowing the name of the class and the name of the method it wants to invoke later, using an object it has in hand. This can be achieved as follows:
public Func<object, object> RecordMethod(string typeName, string methodName)
{
var type = Type.GetType(typeName);
var method = type.GetMethod(methodName);
return (object o) => method.Invoke(o, new object[0]);
}
var method = RecordMethod("MemberRequestDTO", "RecordsToRetrieve");
// later that day ...
MemberRequestDTO someObj = ...;
var result = method.Invoke(someObj);
This is fine if you need to work with type names and method names dynamically, e.g. from user input. Note that this approach requires the use of object throughout, and will only work with a method that takes no parameters. Also note that in this way the type cannot be guaranteed to have a no-arg constructor, so the caller must provide the object himself.
Interpretation #2: The caller starts out knowing the actual class and the actual method it wants to invoke later, using an object that can be constructed later. This can be achieved as follows:
public Func<TOutput> CaptureMethod<TInput, TOutput>(Func<TInput, TOutput> method)
where TInput : new()
{
return () =>
{
var source = new TInput();
return method(source);
};
}
var capturedMethod = (MemberRequestDTO dto) => dto.RecordsToRetrieve();
// later that day ...
var result = capturedMethod();
This captures a known method and returns a function which, when invoked, will instantiate your class and call the method on it. This is a more static approach (the caller knows more than in the previous example) and is able to enforce a constraint that the type being worked with must have a no-arg constructor.
I don't know if I've answered your question but this should at least give you some ideas.
I'm pretty stumped with this so if anyone has any ideas. I have the generic method
public void Foo<TClass>(TClass item) where TClass : class
{ }
And I want to call this method from another generic method, but this generic method doesn't have the type constraint "where TClass : class"
public void Bar<T>(T item)
{
this.Foo<T>(item);
}
This doesn't work, I get the error
"The type 'T' must be a reference type in order to use it as parameter 'TClass'"
Which I understand. But my question is this - is there anything I can do with C# syntax in order to "filter" the generic type "T" to pass it to "this.Bar" if it is a class. Something like....
public void Bar<T>(T item)
{
if (typeof(T).IsClass)
this.Foo<T **as class**>();
}
I realise I could use reflection to call Foo, but this just seems like cheating. Is there something I can do with C# to pass "T" on with the constraint at runtime?
Also - I can't change the constraint on the method "Bar" as it comes from an interface so the constraint has to match the constraint on the interface
The only way to call Foo without reflection, is to cast item to one of the types/classes in its hierarchy (after the proper IsClass check).
Obviously, there's only one type in its hierarchy that you know of a priori: Object.
public void Bar<T>(T item)
{
if (typeof(T).IsClass)
this.Foo((object) item);
}
Edit :
Also, in one of the comments you said you added the class constraint to be to instantiate T. You don't need that, what you need is the new constraint.
Unfortunately there is no way to do this without changing Bar to have the generic constraint class or using reflection. In order to compile C# must know at compile time that T is indeed a class value. There is no way to use a dynamic test such as typeof(T).IsClass in order to satisfy this compile time constraint.
You mentioned in the question that you can't change Bar but it seems like you are willing to accept the possibility of dynamic failure. Perhaps instead change Foo to not have the constraint but instead throw an exception when T is not a class type
if (typeof(T).IsClass)
{
this.GetType()
.GetMethod("Foo", System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public)
.Invoke(this, new object[] { item });
}
I believe there is no way to make it compile. You will have to use reflection to make the call.
Actually. You could cheat if you contain it within a class:
public class Container<T>
{
public Container(T value)
{
Value = value;
}
public T Value { get; private set; }
}
public void Bar<T>(T item)
{
this.Foo<Container<T>>(new Container<T>(item));
}
but this adds one layer you need to call-through and makes the types less clear.