Why can I not infer an interface from a constrained generic collection? - c#

I have a piece of code that works like this:
public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members )
{
// do some stuff to members
return members;
}
However I am confused as to why I can't do this:
public IEnumerable<T> ExecuteFlubversion<T>( IEnumerable<T> memberList ) where T: class,ICacheMember
{
return Flubvert( memberList );
}
Surely the constraint on the generic should guarantee that memberListis an IEnumerable of the ICacheMembertype? Do I really need to convert a collection of existing ( but implicit ) ICacheMember objects into explicit ICacheMember objects and then convert them back afterwards? I can understand that I might need to convert them back given the method signature of Flubvert but I don't see why I should have to convert them in the method call. This is what I am doing in the working code but it seems completely out of keeping with the generally elegant behaviour of generics so I think I must be misunderstanding something about how this is supposed to operate.

First of all covariance of IEnumerable<out T> (and other generic types) only works when T is a reference type, so you need:
public IEnumerable<ICacheMember> ExecuteFlubversion<T>(IEnumerable<T> memberList)
where T: class, ICacheMember // NOTE 'class'
{
var flub = Flubvert(memberList); // can you call with 'memberList'?
return flub; // can you return that type?
// depending on what 'Flubvert' does, maybe return 'IEnumerable<T>'
// and say:
// return (IEnumerable<T>)flub;
}
Also note that I changed the return value. The C# compiler cannot guarantee that the returned object from the non-generic Flubvert method is anything more specific than IEnumerable<ICacheMember>.

Lets say you have:
interface ICacheMemberSub : ICacheMember
{
...
}
And you call your function like this:
ExecuteFlubversion<ICacheMemberSub>(cacheMember);
This function will try to return an object with type IEnumerable<ICacheMember>, and that is not necessarily castable to IEnumerable<ICacheMemberSub>, hence the error.

At risk of not directly answering the question, can you change the signature of Flubvert to a generic? If you make Flubvert generic, the rest of the method code will stay the same and you can still assume that the members will be implementers of ICacheMember.
public IEnumerable<T> Flubvert<T>(IEnumerable<T> members)
where T : class, ICacheMember
{
// do some stuff to members
return members;
}
public IEnumerable<T> ExecuteFlubversion<T>(IEnumerable<T> memberList)
where T : class,ICacheMember
{
return Flubvert(memberList);
}

Related

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.

C# casting generic parameter to interface

I need help with casting generic paremetrs down to an interface.
I have prebaked code like this:
public interface InterFoo<T> {...}
public InterFoo<T> specialFoo<T>() where T : InterFoo<T> {...}
public InterFoo<T> regularFoo<T>() {...}
and i want to implement something like this
public InterFoo<T> adaptiveFoo<T>()
{
if (T is InterFoo<T>)
return specialFoo<T as InterFoo>();
return regularFoo<T>();
}
at this point I cant find any solution so anything would be helpful, thanks.
EDIT: originally the functions had returned an int but that has a simpler solution that is incompatible with the code's intended purpose, the functions have been changed to request a generic type.
The is and as operators only compile for types that the compiler knows can be null (nullable value types or reference types).
You can try a call to IsAssignableFrom:
public int adaptiveFoo<T>()
{
if (typeof(InterFoo<T>).IsAssignableFrom(typeof(T))
return specialFoo<InterFoo>();
return regularFoo<T>();
}
** Update to reflect changes in question **
Type constraints are, unfortunately viral, in order for your method to compile (when keeping with strict type checking from the compiler) you would need the constraint to be added to this method also. However, reflection can circumvent this restriction:
Your method would be:
public InterFoo<T> adaptiveFoo<T>()
{
if (typeof(InterFoo<T>).IsAssignableFrom(typeof(T))
{
var method = typeof (Class1).GetMethod("specialFoo");
var genericMethod = method.MakeGenericMethod(typeof(T));
return (Interfoo<T>)method.Invoke(this, null);
}
return regularFoo<T>();
}

Why can't I use the enumerator of an array, instead of implementing it myself?

I have some code like this:
public class EffectValues : IEnumerable<object>
{
public object [ ] Values { get; set; }
public IEnumerator<object> GetEnumerator ( )
{
return this.Values.GetEnumerator ( );
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ( )
{
return this.GetEnumerator ( );
}
}
But the compiler complains saying:
"Cannot implicitly convert type
'System.Collections.IEnumerator' to
'System.Collections.Generic.IEnumerator'.
An explicit conversion exists (are you
missing a cast?)"
I thought the Array type implemented both IEnumerable interfaces, does it not? Because I can use Linq features on the Values instance directly.
This is a subtle and a bit unfortunate. The easy workaround is:
public IEnumerator<object> GetEnumerator ( )
{
return ((IEnumerable<object>)this.Values).GetEnumerator ( );
}
I thought the Array type implemented both IEnumerable interfaces, does it not?
The rules are:
System.Array implements IEnumerable "implicitly", with public methods.
every array type T[] inherits from System.Array.
every array type T[] implements IList<T>, IEnumerable<T> and so on.
therefore every array type T[] is convertible to IEnumerable<T>
Notice that the third point was NOT
every array type T[] implements IList<T>, IEnumerable<T> and so on with public methods and properties defined on T[] that implicitly implement the members
And there you go. When you look up GetEnumerator, we look it up on object[] and don't find it, because object[] implements IEnumerable<object> explicitly. It is convertible to IEnumerable<object>, and convertibility doesn't count for lookups. (You wouldn't expect a method of "double" to appear on int just because int is convertible to double.) We then look at the base type, and find that System.Array implements IEnumerable with a public method, so we've found our GetEnumerator.
That is, think about it like this:
namespace System
{
abstract class Array : IEnumerable
{
public IEnumerator GetEnumerator() { ... }
...
}
}
class object[] : System.Array, IList<object>, IEnumerable<object>
{
IEnumerator<object> IEnumerable<object>.GetEnumerator() { ... }
int IList<object>.Count { get { ... } }
...
}
When you call GetEnumerator on object[], we don't see the implementation that is an explicit interface implementation, so we go to the base class, which does have one visible.
How do all the object[], int[], string[], SomeType[] classes get generated "on the fly"?
Magic!
This is not generics, right?
Right. Arrays are very special types and they are baked in at a deep level into the CLR type system. Though they are very similar to generics in a lot of ways.
It seems like this class object [] : System.Array is something that can't be implemented by a user, right?
Right, that was just to illustrate how to think about it.
Which one do you think is better: Casting the GetEnumerator() to IEnumerable<object>, or just use foreach and yield?
The question is ill-formed. You don't cast the GetEnumerator to IEnumerable<object>. You either cast the array to IEnumerable<object> or you cast the GetEnumerator to IEnumerator<object>.
I would probably cast Values to IEnumerable<object> and call GetEnumerator on it.
I will probably use casting but I am wondering if this is a place where you or some programmer who could read the code, would think it's less clear.
I think it's pretty clear with the cast.
when you said implicit implementation, you mean in the form of Interface.Method, right?
No, the opposite:
interface IFoo { void One(); void Two(); }
class C : IFoo
{
public void One() {} // implicitly implements IFoo.One
void IFoo.Two() {} // explicitly implements IFoo.Two
}
The first declaration silently implements the method. The second is explicit about what interface method it implements.
What's the reason for implementing IEnumerable<T> like that, instead of implicit implementation with public methods? I got curious because you said "This is a subtle and a bit unfortunate", so it seems like it's because of an older decision that forced you to do this I imagine?
I don't know who made this decision. It is kind of unfortunate though. It's confused at least one user -- you -- and it confused me for a few minutes there too!
I would have thought the Array type would be something like this: public class Array<T> : IEnumerable<T> etc. But instead there is some magical code about it then, right?
Right. As you noted in your question yesterday, things would have been a lot different if we'd had generics in CLR v1.
Arrays are essentially a generic collection type. Because they were created in a type system that did not have generics, there has to be lots of special code in the type system to handle them.
Next time you design a type system put generics in v1 and make sure you get strong collection types, nullable types and non-nullable types baked in to the framework from the beginning. Adding generics and nullable value types post hoc was difficult.
You have to cast the array to IEnumerable<object> to be able to access the generic enumerator:
public IEnumerator<object> GetEnumerator() {
return ((IEnumerable<object>)this.Values).GetEnumerator();
}

Why does this generic method require T to have a public, parameterless constructor?

public void Getrecords(ref IList iList,T dataItem)
{
iList = Populate.GetList<dataItem>() // GetListis defined as GetList<T>
}
dataItem can be my order object or user object which will be decided at run time.The above does not work as it gives me this error
The type 'T' must have a public parameterless constructor in order to use it as parameter 'T' in the generic type
public void GetRecords<T>(ref IList<T> iList, T dataitem)
{
}
What more are you looking for?
To Revised question:
iList = Populate.GetList<dataItem>()
"dataitem" is a variable. You want to specify a type there:
iList = Populate.GetList<T>()
The type 'T' must have a public
parameterless constructor in order to
use it as parameter 'T' in the generic
type GetList:new()
This is saying that when you defined Populate.GetList(), you declared it like this:
IList<T> GetList<T>() where T: new()
{...}
That tells the compiler that GetList can only use types that have a public parameterless constructor. You use T to create a GetList method in GetRecords (T refers to different types here), you have to put the same limitation on it:
public void GetRecords<T>(ref IList<T> iList, T dataitem) where T: new()
{
iList = Populate.GetList<T>();
}
Your revised question passes in dataItem as an object of type T and then tries to use it as a type argument to GetList(). Perhaps you pass dataItem in only as a way to specify T?
If so, the you may want something like so:
public IList<T> GetRecords<T>() {
return Populate.GetList<T>();
}
Then you call that like so:
IList<int> result = GetRecords<int>();
The issue with demanding a public, parameterless constructor can only be because Populate.GetList demands it - i.e. has the "T : new()" constraint. To fix this, simply add the same constraint to your method.
Actually, I doubt that ref is a good strategy here. At a push, out might do (since you don't read the value), but a far simpler (and more expected) option is a return value:
public IList<T> GetRecords<T>(T dataItem) where T : new()
{ // MG: what does dataItem do here???
return Populate.GetList<T>();
}
Of course, at that point, the caller might as well just call Populate.GetList directly!
I suspect you can remove dataItem too... but it isn't entirely clear from the question.
If you don't intend it to be generic (and dataItem is the template object), then you can do this via MakeGenericMethod:
public static IList GetRecords(object dataItem)
{
Type type = dataItem.GetType();
return (IList) typeof(Populate).GetMethod("GetList")
.MakeGenericMethod(type).Invoke(null,null);
}
You can use Generic with < T > that will accept the type in runtime like you want.
Getrecords<T> ...
This should have any more detailed information that you need.
http://msdn.microsoft.com/en-us/library/twcad0zb(VS.80).aspx

.NET: How to check the type within a generic typed class?

How do I get the type of a generic typed class within the class?
An example:
I build a generic typed collection implementing ICollection< T>. Within I have methods like
public void Add(T item){
...
}
public void Add(IEnumerable<T> enumItems){
...
}
How can I ask within the method for the given type T?
The reason for my question is: If object is used as T the collection uses Add(object item) instead of Add(IEnumerable<object> enumItems) even if the parameter is IEnumerable. So in the first case it would add the whole enumerable collection as one object instead of multiple objects of the enumerable collection.
So i need something like
if (T is object) {
// Check for IEnumerable
}
but of course that cannot work in C#. Suggestions?
Thank you very much!
Michael
You can use: typeof(T)
if (typeof(T) == typeof(object) ) {
// Check for IEnumerable
}
Personally, I would side step the issue by renaming the IEnumerable<T> method to AddRange. This avoids such issues, and is consistent with existing APIs such as List<T>.AddRange.
It also keeps things clean when the T you want to add implements IEnumerable<T> (rare, I'll admit).
If you want to use the is operator in a generic class/method you have to limit T to a reference type:
public void MyMethod<T>(T theItem) where T : class
{
if (theItem is IEnumerable) { DoStuff(); }
}

Categories

Resources