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>()
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 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.
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.
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");
I've got a problem with inheritance and generics.
This is the code that illustrates my problem:
namespace TestApplication
{
public class MyClass<T>
{
private T field;
public MyClass(T field)
{
this.field = field;
}
}
public class MyIntClass : MyClass<int>
{
public MyIntClass(int field)
: base(field)
{
}
}
}
And when I try to do something like this:
MyClass<int> sth = new MyClass<int>(10);
MyIntClass intsth = (MyIntClass) sth;
I receive cast exception: Invalid cast exception. Unable to cast 'TestApplication.MyClass`1[System.Int32]' to 'TestApplication.MyIntClass'.
What is more I cannot create cast operator:
public static implicit operator MyIntClass(MyClass<int> myClass)
because: 'TestApplication.MyIntClass.implicit operator TestApplication.MyIntClass(TestApplication.MyClass)': user-defined conversions to or from a base class are not allowed
I need to create casts as described above. I don't know why I cannot cast from a type that is the base class. How can I solve this problem?
Thanks in advance.
Edit
Thanks for Your answers.
Now I see that i cannot convert from a base class to derived class and i see that it doesn't have anything to do with generics.
But why i cannot create user-defined conversions from a base class? I have a method that returns the base class. I am able to define a conversion method but creating a cast operator imho would be a better solution.
You can only cast from a base class to a derived class if the object is actually of type derived class. I mean, you can't cast an instance of base (MyClass<int>) to MyIntClass. You can, however cast it if it was actually of type MyIntClass stored as an MyClass<int> instance.
MyClass<int> foo = new MyIntClass();
MyIntClass bar = (MyIntClass)foo; // this works.
Assume:
class Base {
int x;
}
class Derived : Base {
int y;
}
Base foo = new Base();
Derived bar = (Derived)foo;
if it was allowed, what would the value of bar.y be?
In fact, converting from Derived to Base is not a conversion at all. It's just telling the compiler to let the variable of type Base to point to an object of type Derived. It is possible since derived has more or equal features than Base which is not the case in the other way around.
If you were able to create a conversion operator between base and derived classes, the C# compiler would be unable to distinguish it from the built in relationships defined for them. This is why you cannot create cast operators along inheritance hierarchies.
The other answers so far are correct, but I'd like to point out that your example has nothing to do with generics. It's the equivalent of:
using System;
class Base {}
class Child : Base {}
class Test
{
static void Main()
{
Base b = new Base();
// This will throw an exception
Child c = (Child) b;
}
}
In the comments you asked:
But why conversion from a base class is not allowed?
Simple - it would make no sense. Consider the example:
class BaseClass
{
public int x;
public BaseClass(int StartX)
{
this.x = StartX;
}
}
class ChildClass: BaseClass
{
public int Y;
public BaseClass(int StartX, StartY): base(StartX)
{
this.y = StartY;
}
}
class Program
{
public static void Main()
{
BaseClass B = new BaseClass(3);
ChildClass C = (ChildClass)B;
Console.WriteLine(C.y);
}
}
What do you suppose this program would output, assuming the cast worked? Even worse - imagine that BaseClass has two child classes - ChildClassA and ChildClassB. Do you want this to work?
ChildClassA A = new ChildClassA();
BaseClass bc = (BaseClass)A;
ChildClassB B = (ChildClassB)bc;
This would effectively allow to cast ChildClassA instances to ChildClassB - completely wrong.
As Mehrdad stated, you cannot downcast an object. Upcasting is implicit, therefore you cannot overwrite it.
As for the implicit operator, you can still create a constructor in the derived class which receives a parameter of type baseclass.
If you need to cast freely, define the variable as baseclass, but instantiate derived classes.
As has been said, you're trying to cast an object into a type that it doesn't derive from. Did you perhaps want to do this:
MyClass<int> sth = new MyIntClass(10);
MyIntClass intsth = (MyIntClass) sth;
Instead of creating an MyIntClass, try an alias:
using MyClass<int> = What.Ever.Namespace.MyIntClass;
This is now valid:
MyClass<int> foo = new MyClass<int>();
MyIntClass bar = (MyIntClass)foo;
Just understand that when doing the using alias, you have to qualify your namespace on the alias type name (What.Ever.Namespace).
Regarding your second question:
But why i cannot create user-defined conversions from a base class?
Well, suppose you have this scenario
class Base {
}
class Derived {
public static operator Derived(Base b) { ... }
}
and you tried to do this
Base x = new Derived();
Derived y = (Derived)x;
should the conversion be called? Of course not! The value inside x is actually of type Derived, so the cast is direct, without conversion. But if the value was not of type Derived, but a concrete Base, then the user-defined conversion has to happen because otherwise we'd have a compiler error. This all makes no sense; user-defined conversions are found in compile-time, and the type of the value of x is only known in runtime. Therefore, the compiler would not know what to do - call the user-defined conversion or simply cast the value...
Hope this makes a bit of sense to you.
Answering to your last edit.
This code does already compile, it only fails at runtime:
MyIntClass intsth = (MyIntClass) sth;
So, the following cast operator would be redundant if left explicit:
public static implicit operator MyIntClass(MyClass myClass)
So, the compiler should prevent you from adding that conversion. I think the error might be confusing, but I think it just forbids converting class B to class A if B is derived from A (the warning seemed to me to prevent any conversion to A, at first).
If the operator is made implicit, it is also dangerous, because a downcasting can always fail, so you have to:
show the compiler that you know that, by adding an explicit cast;
show the reader (which includes yourself, minutes later) that the operation might fail.
Assignment/conversion of a base class to a derived class makes sense if you consider assignment or conversion to be a value by value copy. What's confusing about c# for
newbies is the inconsistent way it does things:
'int' is a 'simple' type:
int i = 5; // <- this creates an int.
int j = i; // <- this creates another int and copies the value of i into j.
'Object' is not a simple type:
Object a; // <- this does not create a copy of 'Object', only a reference to one
Object b = new Object(); // <- this actually creates an Object
a = b; // <- this sets the reference to an object a to the reference to an object b.
// both of them reference the same Object. No values were copied.
If it were doing a copy of values then copying to base class to a derived class would
work. C# doesn't work like other languages.
I think that might be what's confusing you.