属性/通用访问的 c# 协方差泛型?

c# Covariance Generics on Properties / Generic Access?

public interface IFoo { }
public abstract class IFooDerived : IFoo { }
public class Foo : IFooDerived { }

public interface IBar { }
public abstract class IBarDerived : IBar { }
public class Bar : IBarDerived { }

public interface IExample<out A, B> where A : IFoo where B : IBar
{
    A Foo(B b);
}

public class Example : IExample<Foo, Bar>
{
    static Example example = new Example();
    static Example()
    {
        Example ex = example;

        IExample<IFoo, IBar> ex_huh = ex; //How can I do this?

        //Or is there a way where I can say I don't care, but I still want to reference ex, like this:
        IExample<object,object> ex_huh2 = ex;
        //Or:
        IExample<, > ex_huh2 = ex;

        ex_huh2.Foo(new Bar()); //And still call Foo?
    }

    public Foo Foo(Bar b)
    {
        throw new NotImplementedException();
    }
}

在上面的示例中,如何在不知道其泛型的情况下向下转换并引用静态示例变量,但仍然能够调用 "A Foo(B b)"?

有强类型的方法吗?

IExample<IFoo, IBar> ex_huh = ex; //How can I do this?

你不能,因为它会破坏类型安全。那是因为你试图让你的方法的参数类型(Foo(B b) 的那个参数)更通用,这是不安全的。

为了更容易理解为什么这是一个问题,让我们将您的示例简化为等效的东西(就该方法参数而言):

function DoSomethingWithFoo(Foo foo) { ... }
// The following line won't compile, but let's pretend it does and see what happens
Action<IFoo> doSomethingWithIFoo = DoSomethingWithFoo;
doSomethingWithIFoo(new OtherFoo());

其中 OtherFoo 是实现 IFoo 的其他 class,但不是 Foo class.

的后代

糟糕!您正在调用 DoSomethingWithFoo,它需要一个 Foo 的实例,但却传递了一个 OtherFoo!古怪的闹剧接踵而至。

这基本上就是您在示例中尝试做的事情。您正在尝试采用 class 方法,该方法需要 Foo 类型的参数,并将其转换为允许您传递任何 IFoo 的方法。

(这就是编译器不允许您将 IExampleB 类型参数声明为 out B 的原因,这就是让您强制转换的原因IExample<..., Foo>IExample<..., IFoo>。编译器认为 B 类型参数用作方法参数的类型,因此,使其协变会破坏类型安全。)


至于如何完成您正在寻找的东西:这将取决于。你想做的具体例子

IExample<...figure out how to declare this...> = ...any IExample<A, B>...;
ex_huh2.Foo(new Bar());

通常不会起作用,因为“...任何 IExample<A, B>...” 很可能是 IExample<..., OtherBar>,然后您不能将它传递给 new Bar()。你必须弄清楚你想如何解决这个冲突。

也许您真的想传入一个 new Bar(),在这种情况下,您可能想在一个方法中执行此操作,该方法仅限于将 IExampleBar 作为他们的第二个类型参数:

public void AddABar<TFoo>(IExample<TFoo, Bar> example)
{
    example.Foo(new Bar());
}

或者您可能想创建第二个类型参数的新实例:

public void AddAThing<TFoo, TBar>(IExample<TFoo, TBar> example)
    where TBar : new()
{
    example.Foo(new TBar());
}

或者您可能想让 IExample<A, B> 从声明非泛型 Foo(IBar b) 的非泛型 IExample 派生,在 [=39] 上实现该非泛型方法=] class,并在该方法内对 B 进行类型转换——如果您在运行时将错误的类型传递给该方法,您将面临 InvalidCastException 的风险。

这真的完全取决于你想如何解决 "I want to cast this to something I can pass a new Bar() to" 的冲突,而事实上 IExample<A, B> 的任何给定实现都不一定能够接受 new Bar() 作为方法参数。