I noticed a strange behaviour with generic classes using a list of interfaces as a constructor parameter.
Let's say we have the following class
public class GenericClass<T> where T : IInterface
{
public GenericClass(int inInt, List<T> inList){
}
public GenericClass(int inInt, object inObject){
}
}
When I try to create an instance like this (tmpType implements IInterface):
IEnumerable<IInterface> tmpSomeObjects = xy;
Activator.CreateInstance(typeof(GenericClass<>).MakeGenericType(tmpType), 5, (List<IInterface>)tmpSomeObjects);
The second constructor will be called (int, object).
I probably miss an important point... I expected the first constructor to be executed.
Your IEnumerable is of type IEnumerable<IInterface>, but the type you are constructing has a generic parameter of a derived type, so it does not match the exact constructor.
Say T is Foo (which implements IInterface), your type becomes:
public class GenericClass<Foo>
{
public GenericClass(int inInt, List<Foo> inList){
}
public GenericClass(int inInt, object inObject){
}
}
Yet you are passing an IEnumerable<IInterface> (or List<IInterface>) to it, which doesn't match List<Foo>, so that's why it's preferring object (not only it's preferred... the other constructor won't match at all).
Try it: remove the constructor with object and try to do this:
var list = new List<IInterface>();
var x = new GenericClass<TypeImplementingIInterface>(5, list);
That won't even compile.
So the solution in your case would be simple... make the parameter in the constructor IEnumerable<IInterface>, instead of List<T>, which is what you actually want to pass it
You are trying to do this:
var list = new List<IInterface>();
new GenericClass<TmpType>(5, list);
However, List<IInterface> is not convertible to List<TmpType>, even though TmpType implements IInterface, so overload with object is chosen.
If you try with:
var list = new List<TmpType>();
// should work with Activator.CreateInstance too
new GenericClass<TmpType>(5, list);
Then it should choose first one.
Note that with Activator.CreateInstance, unlike "manual" invocation, runtime type of list does matter. So for example, this:
IEnumerable<IInterface> list = new List<TmpType>();
Activator.CreateInstance(typeof(GenericType<>)).MakeGenericType(typeof(TmpType)), 5, list);
will choose first overload, because runtime-type is List<TmpType>. However this:
IEnumerable<IInterface> list = new List<TmpType>();
new GenericClass<TmpType>(1, list);
will chose second (with object), because now constructor is resolved at compile time.
Related
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 have a class that takes a generic as a parameter like this
public MyNewClass(string text, T myClass, int f, bool test = false)
The compiler is complaining about T myClass.
I know I can pass "defined" generic classes to a class constructor (such as List, Dictionary etc) and have seen that this can be done in C# as well, but can't find a reference to it.
You should declare the generic parameter, when you declare your class.
public class MyNewClass<T>
{
}
Then this parameter could be accessible from any of the class's methods. When you will create an instance of your MyNewClass, you should define also the type of T, for instance:
var instanceOfMyNewClass = new MyNewClass<className>(text, classIntance, f, true);
where classInstance is an instance of an object of type className.
A good introduction about generics is here.
I suspect that the issue you are facing is that the following does not compile
public class Foo<T>
{
public Foo(string s, T t) { }
}
var foo = new Foo("Hello", new Something());
The fix to this is to specify in the constructor.
var foo = new Foo<Something>("Hello", new Something());
However, this still seems a little strange given that normally, the C# compiler can infer the type of T.
The problem here is that the C# compiler is only allowed to infer generics on the first parameter of a method. So the following IS allowed.
public class Foo<T>
{
public Foo(T t, string s) { }
}
var foo = new Foo(new Something(), "Hello");
One thing that irritates me about Java is the awful implementation of compile-time translation of generic type arguments.
I can observe and understand that the C# implementation is far better, but I'm confused as to how it works.
Essentially, how can you say:
T t = new T()
If you don't know the type of T and therefore don't know the constructor argument requirements?
I can see
Class<T> cl = T.class
or
T[] tarr = new T[0]
but I don't see how you can really create a new instance of T if you don't know the requirements of constructing it?
You can only do new T(); if T is constrained to have a plain, parameterless public constructor, for instance:
public class Foo<T> where T : new() {
private myT = new T();
}
Additionally, there is no way to specify that any other sort of constructor exist. This is not possible:
// Doesn't work
public class Foo<T> where T : new(String, Int) {
private myT = new T("Foo", 5);
}
To your other points, this is how you get the type of T at runtime:
var tType = typeof(T);
and creating an array of T doesn't actually create any instances (unless T is a value type, in which case it creates the default value of that type):
// Space for 32 T's, but nothing in the array.
// If T is a value type, like an int for instance,
// each one would have the default value (0 for int, for example)
var arrayOfT = new T[32];
You cannot say new T() unless you constrain your generic type to have a parameterless constructor using the where T : new() constraint — see Constraints on Type Parameters.
And there are no “constructor argument requirements”, since the only supported constructor is the parameterless one. You cannot use, say, new T(false) — constraints of the form where T : new(bool) are not allowed.
Actually you ask the compiler to force T to have a parameterless constructor so he knows you can new T(). For example:
class Test<T>
{
T Create()
{
return new T();
}
}
It won't compile because the compiler can't be sure that T won't be an abstract class and that it'll have a default constructor. To make it works you have to add a constrain on the real type of T:
class Test<T> where T : new()
Now the compiler will force T to be a non abstract class with a default constructor. For example this code is not valid because the given type is abstract:
abstract class AnotherTest
{
public void Test()
{
Test<Derived> test = new Test<Derived>();
}
}
Again, if you try to use a class without default constructor the compiler will emit an error:
class AnotherTest
{
public AnotherTest(string someParameter)
{
}
public void Test()
{
Test<Derived> test = new Test<Derived>();
}
}
With an array it's a little bit different. Actually you simply ask the compiler to reserve the memory for a given number of slots, you do not allocate the memory for that objects (in case of reference types it'll simply put null in each slot).
References on MSDN
Introduction to generics
Generic type constrain
The new constrain
new T() is just a syntactical sugar for Activator.CreateInstance<T>()
Lets say I have an interface passed to my method:
public void AlphaToChar(iList _blah)
{
}
Out of IList I want to extract it's members Type and use its type to create other Arrays or Lists in the method. See example below.
The "List = new List();" part doesn't work because, as I assume it's a type variable, not the actual type.
Any way around this ? How can I accomplish this and create a new collection of an extracted Type?
Type[] listTypes = list.GetType().GetGenericArguments();
Type listType = null;
if (listTypes.Length>0)
{
listType = listTypes[0];
}
List<listType> = new List<listType>();
Thank you.
You can do the List<> construction using the following:
// Find the generic argument type of your old list (theList)
Type genericType = theList.GetType().GetGenericArguments()[0];
// Create a new List with the same generic type as the old one
Type newListType = typeof(List<>).MakeGenericType(genericType);
// Create a new instance of the list with the generic type
var instance = Activator.CreateInstance(newListType);
But it's only going to work if you are using generic lists. The example you gave was using a regular IList. You would have to change your method signature to use a generic IList<>:
public void AlphaToChar(IList<Something> _blah) { }
Or make it even more generic:
public void AlphaToChar<T>(IList<T> _blah) /* where T : ISomething, new() */ {}
Without doing so, you should know what your IList is going to contain and you wouldn't have to use reflection to figure out what its elements types are.
This dynamically constructs a generic List<T> for the specified element type:
IList list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
Note that the result variable is not statically typed to the specialized list, since you don't know the type at compile time. As such, it's not possible for it to be statically typed. You're taking advantage of the fact that List<T> also implements IList here.
System.Collections.IList list =
Activator.CreateInstance(typeof(List<>)
.MakeGenericType(listTypes[0])) as System.Collections.IList;
I want something like this:
class Foo<T>{...}
class Boo<T>{
Queue<T> stuff = new Queue<T>();
public void Boo(Foo<T>){...};
}
...
//Extract the generic type - string - to define the type
//of MyBoo.
var MyBoo = new Boo(new Foo<string>());
I get the error "generic type 'Boo' requires '1' type arguments. Ya, I fixed the problem by stating the template type explicitly, but I'd like to know if there was/is a way to extract that type implicitly, rather than having to state it explicitly.
This other post may be related, but I'm not sure.
You can't do it implicitly directly with the constructor of a generic type, but you could from a generic method, e.g. in a non-generic class:
public static class Boo
{
public Boo<T> Create<T>(Foo<T> foo)
{
return new Boo<T>(foo);
}
}
Then:
// myBoo will be inferred to be of type Boo<string>
var myBoo = Boo.Create(new Foo<string>());
Of course, it doesn't have to be another class called Boo - it could be something completely different, and it could be an instance method of something else:
var factory = new BooFactory();
var myBoo = factory.Create(new Foo<string>());
The important point is that it's a generic method - type arguments can be inferred for generic methods, but not for generic types.
Type inference works only with methods. So if you have generic method and it is clear how to substitute generic parameter it will be substituted by compiler. For new operator it doesn't work so consider creating factory method like Create that will produce instances of Boo. Otherwise impossible.