Reputation: 77
I'm aware that multiple similar sounding questions have been asked before, however I still believe that this particular problem has not been solved yet.
There is a class Super
and a generic class Foo<T> : Super
, which both come from a third-party library. I need to extend the functionality of Foo<T>
so I have this generic class:
public class Bar<T, U> : Foo<T> {
public void DoSomething() { ... }
// Other things that use T and U
}
I now want to have a collection(list) of "Bar's" and be able to call DoSomething on all of them. So logically, I dont care about the type argument of Bar, when calling the void method as it is irrelevant in that scenario.
Additionally the third-party library exposes methods such as:
public LibraryMethod(Super super) { ... }
And I need to be able to call that method with any of my "Bar's".
The reason why I cant use an interface is, because the third-party library expects a Super
in some of the methods it exposes, and an interface can be implemented by any class, so I cannot use my Bar's as Super's even though every Bar (no matter what its type arguments are) must be a Super. The interface essentially ruins the inheritance chain here and I can no longer pass my "Bar's" to methods expecting a Super.
Obviously I cannot make Bar<T, U>
inherit from a non-generic base class, as per the same reason as above. It would ruin the inheritance chain as well.
In an ideal situation I could do something like this:
List<Bar<>> bars = new List<Bar<>>();
bars.Add(new Bar<Type1, Type2>());
bars.Add(new Bar<Type3, Type4>());
foreach (Bar<> bar in bars) {
bar.DoSomething();
}
LibraryMethod(bars[0]);
Upvotes: 2
Views: 1712
Reputation: 142288
In short - you can't, C# does not support instantiating instances of open generic types and you can't limit the list to be both Super
and IDoSomething
interface at the same time. And you can't inherit Bar
from some non-generic type (which inherits Super
and implements IDoSomething
) and Foo<>
in addition.
In this particular case you can add AsSuper
to your interface:
public interface IDoSomething
{
void DoSomething();
Super AsSuper();
}
Which should be easily implemented in Bar
:
public class Bar<T, U> : Foo<T>, IDoSomething {
public void DoSomething() { ... }
public Super AsSuper() => this;
// Other things that use T and U
}
Which will allow you to type safely invoke LibraryMethod
:
LibraryMethod(bars[0].AsSuper());
Upvotes: 1
Reputation: 899
You cannot do what you describe. replace your bar class by List or KeyValuePair to see what it result in:
var myList = new List<List<>>();
var myListOfKv = new List<KeyValuePair<>>();
you are trying to allocate some memory, the compiler needs to know some constraints. you can maybe try to put some restrictions on types like: class Bar<x,y> :Foo where x is ISoda
you might also think of another way to extend functionnality by adding in your children specific method to handle objects:
class Bar<x>: Foo<x>
{
public void Inject(Iy iy);
public void UseInjected();
}
The third party lib will still accept the Bar instances and ignore new functionnality.
Upvotes: 0
Reputation: 4939
You can lose type information at compile time and track this elsewhere. Maybe you have program knowledge (you, the programmer) and know type information. Otherwise you have to create some data structure and carry this information around. Anyways, you can use interfaces here.
Consider your current situation (does not work):
public class Super { public int SuperData { get; set; } }
public class Foo<T> : Super { public T FooData { get; set; } }
public class Bar<T, U> : Foo<T>
{
public U BarData { get; set; }
public void DoSomething()
{
Console.WriteLine("i do something");
}
}
public static void LibraryMethod(Super super) { Console.WriteLine("i'm super"); }
public void Main()
{
// generic "object" collection
var listy = new List<object>();
listy.Add(new Bar<int, double>());
listy.Add(new Bar<DateTime, bool>());
foreach (var x in listy)
{
x.DoSomething(); /// 'object' does not contain a definition for 'DoSomething'
// and then call your library
LibraryMethod((Super)x);
}
}
Instead, add an interface and implement in your type:
public interface IDoSomething
{
void DoSomething();
}
public class Bar<T, U> : Foo<T>, IDoSomething
{
public U BarData { get; set; }
public void DoSomething()
{
Console.WriteLine("i do something");
}
}
Now you can use with cast to correct interface type:
public void Main()
{
var listy = new List<object>();
listy.Add(new Bar<int, double>());
listy.Add(new Bar<DateTime, bool>());
foreach (var x in listy)
{
var bar = x as IDoSomething;
// check null reference
bar.DoSomething();
// and then call your library
LibraryMethod((Super)x);
}
}
output:
> Main()
i do something
i'm super
i do something
i'm super
Upvotes: 0