Reputation: 1469
I have a class with the following class/interface structures:
public class Foo : IBar<FooBar>, INonGenericBar
{
// class implementation
public override IsGeneric => return true;
public override IsNonGeneric => return true;
}
public IBar<T>
{
bool isGeneric {get;}
}
public INonGenericBar
{
bool IsNonGeneric {get;}
}
I know can get the implementation of INonGenericBar with the is
and as
keywords;
var myFoo = new Foo();
var barIsNonGeneric = myFoo is INonGenericBar;
Assert.IsTrue(barIsNonGeneric);
var nonGenericBar = myFoo as INonGeneric;
Assert.IsTrue(nonGenericBar.IsNonGeneric);
but since IBar is a generic interface, it seems I need to know the exact generic implemenatation
var myFoo = new Foo();
var isGenericBar = myFoo is IBar<FooBar>
var isParentGenericBar = myFoo is IBar<object>
Assert.IsTrue(isGenericBar);
Assert.IsFalse(isParentGenericBar);
var GenericBar = myFoo as IBar<FooBar>;
var GenericParentBar = myFoo as IBar<object>;
Assert.IsNotNull(GenericBar);
Assert.IsNotNull(GenericParentBar); // fails
I am however only intrested in the IBar<> interface itself, not the generic type it uses. Is there a way for me I can cast it to IBar<>?
Upvotes: 1
Views: 795
Reputation: 155250
C# does not support the concept of "generic types, but I don't care about the type parameters" - this is why many libraries with generic classes put all of their non-generic members in a non-generic superclass (or generic interfaces putting their non-generic members in a non-generic parent interface).1
(Oddly enough, Java does support this scenario, but (ironically) this is only because Java's generics are implemented using type-erasure instead of .NET's reified-generics1)
That said, C# does let you get the "open type" of a generic type with typeof
operator - but only in the context of runtime reflection - so you cannot use it in normal program code.
So this is okay:
Type openType = typeof(IBar<>);
But this is not:
IBar<> anyIBar = ...
The only solution in C# is to refactor-move all of the members of IBar
that don't depend on T
to INonGenericBar
.
As per my understanding of the .NET CLR: if any other CLR language wants to support this feature they would have to also implement this via some kind of reflection hack because the CLR itself does not support runtime references to generic objects without type parameters (I might be wrong!)
Apparently Microsoft is aware of requests for support for "wildcard generics" and this GitHub issue is tracking the task - but don't expect this to be implemented for years - considering it took 54 years (from Tony Hoare's 1965 invention of the null reference to C# 8.0's implementation of non-null reference types) so don't hold your breath.
1 This is definitely a design mistake of the C# language and/or the .NET CLR because it effectively breaks generic inheritance (i.e. each generic type Dog<T>
may need a non-generic parent Dog
, but if you have a generic child SamoyedDog<T>
, then that too may need a non-generic parent SamoyedDog
, but C# and the .NET CLR will not allow SamoyedDog<T>
to derive from both SamoyedDog
and Dog<T>
.
2 I consider this ironic[3] because the whole point of reified generics is to have "first-class" generics instead of Java's second-class generics that only matter at compile-time - it's ironic that the end-result of Java's implementation enables (in this case at least) greater flexibility which is the intended result of first-class reified generics. I note (provided this is a CLR limitation) that the .NET CLR and C# could support parameter-less generics in a straightforward manner, but the demand just isn't there because the alternative (i.e. refactoring) is much simpler and the C#/.NET CLR team decided to spend their efforts on other new features.
[3] As someone who is British and not American, I take pride in using irony correctly!
Upvotes: 4
Reputation: 271660
If I understand correctly, you need to make IBar<T>
covariant. After that, you can do this without getting a null:
var GenericParentBar = myFoo as IBar<object>;
To make IBar<T>
covariant, simply add the out
modifier in the generic parameter;
public IBar<out T>
{
bool isGeneric {get;}
}
Note that after this change, you can't add any methods to IBar
that takes a T
as a parameter. You can only add methods that return T
.
To learn more about variance, go to here.
Upvotes: 1