21
public abstract class EntityBase { ... }

public interface IFoobar
{
    void Foo<T>(int x)
        where T : EntityBase, new();
}

public interface IFoobar<T>
    where T : EntityBase, new()
{
    void Foo(int x);
}

public class Foobar<T> : IFoobar, IFoobar<T>
    where T : EntityBase, new()
{
    public void Foo(int x) { ... }

    void IFoobar.Foo<T>(int x) { Foo(x); }
}

我收到编译器警告:Type parameter 'T' has the same name as the type parameter from outer type '...'

我试过做:void IFoobar.Foo<U>(int x) { Foo(x); },但是我不能保证 U 和 T 是一样的。Foobar 类的实现方式,它们必须相同,这一点非常重要。

我也尝试过这样做:void IFoobar.Foo<U>(int x) where U : T { Foo(x); },但这并不能保证 U 和 T 相等,并且它不允许我重新定义约束,因为它是在接口上定义的。

4

3 回答 3

15

最大的问题是您的接口没有很好地定义,并且与您的代码的意图不匹配。

如果您T在界面上不公开可见,那么外部代码甚至不必知道有T. 您需要创建接收或返回的方法T,或者具有一些类型的属性T,或者您应该完全摆脱T,并使您的接口非泛型。

一旦你支持了这一点,你应该更清楚为什么在这里不需要两个不同的接口,你应该不再需要协调它们。

如果事实证明您确实需要一个带有 的版本T和一个非 T 版本,那么更惯用的方法是传递object而不是T

public interface IFoo
{
    void DoSomething(object o);
    object DoSomethingElse();
}

public interface IFoo<T>
{
    void DoSomething(T item);
    T DoSomethingElse();
}

有关此示例,请参见 , ,IEnumerableICollection接口。IList

但仔细考虑。最后的设计折衷(同时具有通用版本和对象版本)总是留下一些不足之处。

你会牺牲其中之一:

  • 直接传达设计合同的良好界面设计(如果您在传入错误的类型时抛出异常或不执行任何操作)
  • 类型安全,以及随之而来的 bug 的减少(如果您正确操作任何旧对象)
于 2011-07-19T00:36:56.817 回答
11

您可以执行以下两项操作之一:

  1. 忽略警告并将两种类型都设为 T。
  2. 进行运行时检查并抛出异常:

    if (typeof(T) != typeof(U)) throw Exception("Not the same type");
    

正如其他人所说,也许您需要重新考虑设计界面的方式。

于 2011-07-19T01:36:36.233 回答
3

试试看嘛

void IFoobar.Foo<U>(int x) { Foo(x); }

当然,这仍然不能保证UT. 你不能在编译时强制执行,因为当你实现一个接口时,你必须遵循它的规则——并且IFoobar不对 施加这样的限制Foo<T>,如果你这样做了,你将不再实现该接口(根据定义,因为你更严格,但你声称你不是)。

您可以尝试在运行时检查它,尽管这有点“作弊”(因为您也没有真正符合接口)。

于 2011-07-19T00:24:39.793 回答