属性/通用访问的 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
的方法。
(这就是编译器不允许您将 IExample
的 B
类型参数声明为 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()
,在这种情况下,您可能想在一个方法中执行此操作,该方法仅限于将 IExample
与 Bar
作为他们的第二个类型参数:
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()
作为方法参数。
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
的方法。
(这就是编译器不允许您将 IExample
的 B
类型参数声明为 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()
,在这种情况下,您可能想在一个方法中执行此操作,该方法仅限于将 IExample
与 Bar
作为他们的第二个类型参数:
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()
作为方法参数。