My question is related to Is there a reasonable approach to "default" type parameters in C# Generics?, but using an inner generic class that approach doesn\'t work
I don't think there's much you can do about it, to be honest. You could make Foo
doubly generic:
public class Foo<TData, TArgs> where TArgs : FooEventArgs<TData>
{
public delegate void EventHandler<TArgs>(object sender, TArgs e);
public event EventHandler<TArgs> Changed;
}
Then you could write:
public class Foo : Foo<object, FooEventArgs>
... but it's really making things very complicated for very little benefit.
I would also say that even though it's a bit more verbose to include the type argument, it does make it very clear - whereas inheritance can muddy the waters in various ways. I'd steer clear of class inheritance when you're not really trying to model behaviour specialization.
The reason your implicit conversion doesn't work has nothing to do with generics, by the way - as the error message states, you can't declare a conversion (implicit or explicit) which goes up or down the inheritance hierarchy. From the C# spec section 6.4.1:
C# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion.
(See that section for more details.)
As a side note, I find it more common to use inheritance the other way round for generics, typically with interfaces:
public interface IFoo
{
// Members which don't depend on the type parameter
}
public interface IFoo<T> : IFoo
{
// Members which all use T
}
That way code can receive just an IFoo
without worrying about the generics side of things if they don't need to know T
.
Unfortunately, that doesn't help you in your specific case.
Another interesting thing I just found is that you can create generic classes with the same name but different signatures.
class Foo<T> {
}
class Foo<T,T> {
}
then you can call either one of them like follows:
new Foo<string>();
new Foo<int,double>();
new Foo<string,int>();
I just thought it was interesting that despite both classes having the same name they can co-exist because they have different signatures.
I guess this is how the Tuple class works
public class Tuple<T1, T2, T3... T8>
{
...