Assign more specific generic type to less specific type - c#

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>.

Related

c# return interface type from method

I have 2 interfaces defined like this
public interface IEnumerableDisposable<out T> : IEnumerable<T>, IDisposable
{
}
public interface IApiCollection
{
IEnumerableDisposable<TItem> GetItems<TItem>();
}
and a method that needs to return the first interface type like this
public IEnumerableDisposable<TItem> GetItems<TItem>()
where TItem can be either type Foo or type Bar
Question how do i cast the return type to IEnumerableDisposable ?
My understanding is that i need class
public class EnumandDisposeFoo : IEnumerableDisposable<Foo>
{
}
and instaciate this class inside my method. how would i then cast the type back to its generic form?
Any help would be greatly appreciated
var foo = new EnumandDisposeFoo(_pagedApi);
var fooitems = foo.GetEnumerator();
return foo; --> errors with
Cannot implicitly convert type 'ApiCollection.Implementation.EnumandDisposeFoo' to 'ApiCollection.IEnumerableDisposable<TItem>'. An explicit conversion exists (are you missing a cast?
Does your code perhaps look similar?
I tried to derive it from the provided error message...
public IEnumerableDisposable<TItem> GetItems<TItem>()
{
var foo = new EnumandDisposeFoo(_pagedApi);
// var fooitems = foo.GetEnumerator(); // not used and is in fact of type `IEnumerator<Foo>`
return foo;
}
If yes, EnumandDisposeFoo implements IEnumerableDisposable<T> while specifying the element type T as Foo. At this point it is no longer generic. Trying to cast (implicitly) to the generic IEnumerableDisposable<TItem> will fail unless you provide an override for that specific implicit cast.
I doubt you want that.
Either you change EnumandDisposeFoo to a generic implementation:
public class EnumandDisposeFoo<TItem> : IEnumerableDisposable<TItem> { }
or you limit your method to return the (non-generic) IEnumerableDisposable<Foo>.
The compiler doesn't know which kind of type you want to return, thus no error at compile time.
Calling your method with GetItems<Foo>() will probably succeed.
But calling your method with any other type than Foo for TItem will try an implicit cast at run-time which might or might not fail.

Method return type like Generic Class Type

Is possible set method return type, like type to generic class ?
I have Generic Class:
class Generic<T>
{
public static DataSet test(T input)
{
//Some logic...
}
}
Another Class, where i call my generic class.
It works for this examples:
Generic<int>.test(10);
But if i want to call different methods, with complex unknown date types, i don't know how i put their date type like Generic Type.
For Example
var data = Data.GetData(); // return List<string,int>
var data2 = Data.GetData2() // return Tuple<List<string>, List<int>>
I try use method GetType, for get returns method type, something like this, but it doesn't work.
Generic<data.GetType()>.test(data);
Is it possible, something like this ?
No, you can't specify the generic type at runtime without reflection, but there may be other ways to solve your problem. You could put the generic constraint on the method instead of the class:
class Generic
{
public static dynamic Test<T>(T input)
{
//Some logic...
}
}
which then can be inferred from the input type:
Generic.Test(data);
Return Type of a function is known in compile time.
Therefore if I understood your question correctly what you're asking for isn't possible. TL;DR You can't set return type in runtime.

List of derived objects

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.

How to assigning an Action of "GenericType" in a Action of a base type of the "GenericType"?

I need assign an Action of "GenericType" in a Action of a base type of the "GenericType".
Therefore, I will have several actions and each of a different ModelMapNode type.
public class ModelMapNode { }
public class ModelMapNode<TModelMap> : ModelMapNode { }
public class Class1
{
private List<Action<ModelMapNode>> Actions { get; set; }
public void AddAction<T>(Action<ModelMapNode<T>> childConfigurer)
{
this.Actions.Add((Action<ModelMapNode>)childConfigurer);
}
}
This type cast does not work.
So, based on your comment, you know that an Action<ModelMapNode<T>> object cannot be safely cast to a (Action<ModelMapNode>). Either you're doing something somewhere else to ensure that this won't ever fail at runtime, or you want some exception to be thrown at runtime in the event that the actual parameter passed to action is not an ModelMapNode<T>.
You can't do this by casting the Action, because the compiler knows it's not safe, but what you can do is create a new wrapper function that casts the parameter and then calls the other action:
public void AddAction<T>(Action<ModelMapNode<T>> childConfigurer)
{
this.ActionOne = node => childConfigurer((ModelMapNode<T>)node);
}
That's fundamentally unsafe.
Had that been legal, you would be able to pass an Action<ModelMapNode<SomeClass>>, then cast it to Action<ModelMapNode> and pass it an incorrect class that it can't handle.
Instead, you should make the entire class generic, then change the field to an Action<ModelMapNode<T>>.
The generic type Action<in T> is contravariant ("in") in its type argument T.
So you could say:
Action<ModelMapNode> a1 = XXX;
Action<ModelMapNode<T>> a2 = a1;
and it would work fine. Because if an action a1 can take in any ModelMapNode then in particular it can tak in a ModelMapNode<T> because any ModelMapNode<> is a ModelMapNode by inheritance (base class).
You are trying to use Action<in T> as if it were covariant, not contravariant, and that can never work.

How does C# do runtime generics?

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>()

Categories

Resources