[英]How do I use a generic class without specifying its type parameters in C#?
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.有一个 class
Super
和一个通用 class Foo<T>: Super
,它们都来自第三方库。 I need to extend the functionality of Foo<T>
so I have this generic class:我需要扩展
Foo<T>
的功能,所以我有这个通用的 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.我现在想要一个“酒吧”的集合(列表),并且能够在所有这些上调用 DoSomething。 So logically, I dont care about the type argument of Bar, when calling the void method as it is irrelevant in that scenario.
所以从逻辑上讲,当调用 void 方法时,我不关心 Bar 的类型参数,因为它在那种情况下是无关紧要的。
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.我不能使用接口的原因是,因为第三方库在它公开的一些方法中期望
Super
,并且接口可以由任何 class 实现,所以我不能将我的 Bar 用作 Super,即使每个 Bar (不管它的类型 arguments 是什么)必须是超级。 The interface essentially ruins the inheritance chain here and I can no longer pass my "Bar's" to methods expecting a Super.该接口基本上破坏了 inheritance 链,我不能再将我的“Bar”传递给期望 Super 的方法。
Obviously I cannot make Bar<T, U>
inherit from a non-generic base class, as per the same reason as above.显然,由于与上述相同的原因,我不能让
Bar<T, U>
从非泛型基础 class 继承。 It would ruin the inheritance chain as well.它也会破坏 inheritance 链。
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]);
You can add method AsSuper
to your interface:您可以将方法
AsSuper
添加到您的界面:
public interface IBar
{
void DoSomething();
Super AsSuper();
}
Which should be easily implemented in Bar
:这应该很容易在
Bar
中实现:
public class Bar<T, U> : Foo<T>, IBar {
public void DoSomething() { ... }
public Super AsSuper() => this;
// Other things that use T and U
}
Which will allow you to type safely invoke LibraryMethod
:这将允许您安全地键入调用
LibraryMethod
:
LibraryMethod(bars[0].AsSuper());
Simply put, you cannot do that easily in a strongly typed language such as C#.简而言之,在 C# 之类的强类型语言中,您不能轻易做到这一点。
Think of it this (simplified) way: a List<T>
needs to know how big T
in memory, so it can reserve enough space for its default number of entries.可以这样想(简化):
List<T>
需要知道 memory 中的T
有多大,因此它可以为其默认数量的条目保留足够的空间。 There is no guarantee to the compiler that Type1
and Type3
are the same size, nor Type2
and Type3
.编译器不能保证
Type1
和Type3
的大小相同,也不能保证Type2
和Type3
。
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:现在您可以使用 with cast 来更正接口类型:
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: output:
> Main()
i do something
i'm super
i do something
i'm super
You cannot do what you describe.你不能做你描述的事情。 replace your bar class by List or KeyValuePair to see what it result in:
用 List 或 KeyValuePair 替换您的酒吧 class 以查看结果:
var myList = new List<List<>>();
var myListOfKv = new List<KeyValuePair<>>();
you are trying to allocate some memory, the compiler needs to know some constraints.您正在尝试分配一些 memory,编译器需要知道一些约束。 you can maybe try to put some restrictions on types like: class Bar<x,y>:Foo where x is ISoda
您可以尝试对以下类型进行一些限制: 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.第三方库仍将接受 Bar 实例并忽略新功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.