繁体   English   中英

如何使用通用 class 而不在 C# 中指定其类型参数?

[英]How do I use a generic class without specifying its type parameters in C#?

我知道之前已经问过多个类似的问题,但是我仍然相信这个特定的问题还没有解决。

有一个 class Super和一个通用 class Foo<T>: Super ,它们都来自第三方库。 我需要扩展Foo<T>的功能,所以我有这个通用的 class:

public class Bar<T, U> : Foo<T> {
  public void DoSomething() { ... }

  // Other things that use T and U
}

我现在想要一个“酒吧”的集合(列表),并且能够在所有这些上调用 DoSomething。 所以从逻辑上讲,当调用 void 方法时,我不关心 Bar 的类型参数,因为它在那种情况下是无关紧要的。

此外,第三方库还公开了以下方法:

public LibraryMethod(Super super) { ... }

而且我需要能够用我的任何“酒吧”调用该方法。

非解决方案 1:接口

我不能使用接口的原因是,因为第三方库在它公开的一些方法中期望Super ,并且接口可以由任何 class 实现,所以我不能将我的 Bar 用作 Super,即使每个 Bar (不管它的类型 arguments 是什么)必须是超级。 该接口基本上破坏了 inheritance 链,我不能再将我的“Bar”传递给期望 Super 的方法。

非解决方案 2:非通用基础 Class

显然,由于与上述相同的原因,我不能让Bar<T, U>从非泛型基础 class 继承。 它也会破坏 inheritance 链。

虚构的理想情况

在理想情况下,我可以这样做:

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]);

您可以将方法AsSuper添加到您的界面:

public interface IBar
{
   void DoSomething();
   Super AsSuper();
}

这应该很容易在Bar中实现:

public class Bar<T, U> : Foo<T>, IBar {
  public void DoSomething() { ... }
  public Super AsSuper() => this;
  // Other things that use T and U
}

这将允许您安全地键入调用LibraryMethod

LibraryMethod(bars[0].AsSuper());

简而言之,在 C# 之类的强类型语言中,您不能轻易做到这一点。

可以这样想(简化): List<T>需要知道 memory 中的T有多大,因此它可以为其默认数量的条目保留足够的空间。 编译器不能保证Type1Type3的大小相同,也不能保证Type2Type3

您可能会在编译时丢失类型信息并在其他地方进行跟踪。 也许你有程序知识(你,程序员)并且知道类型信息。 否则,您必须创建一些数据结构并携带此信息。 无论如何,您可以在这里使用接口。

考虑您目前的情况(不起作用):

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);
    }
}

相反,添加一个接口并在您的类型中实现:

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");
    }
}

现在您可以使用 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:

> Main()
i do something
i'm super
i do something
i'm super

你不能做你描述的事情。 用 List 或 KeyValuePair 替换您的酒吧 class 以查看结果:

var myList = new List<List<>>(); 
var myListOfKv = new List<KeyValuePair<>>(); 

您正在尝试分配一些 memory,编译器需要知道一些约束。 您可以尝试对以下类型进行一些限制: class Bar<x,y>:Foo where x is ISoda

您可能还会想到另一种方法来扩展功能,方法是在您的孩子中添加特定的方法来处理对象:

class Bar<x>: Foo<x>
{
    public void Inject(Iy iy);
    public void UseInjected();
}

第三方库仍将接受 Bar 实例并忽略新功能。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM