4

我有一个通用方法:

public bool DoSomething<T>(T item) where T: IBase
{
    return DoSomethingSpecific(item);
}

public bool DoSomethingSpecific(IBase item)
{
    return true;
}

public bool DoSomethingSpecific(IAdvanced item)
{
    return false;
}

请注意,IAdvanced 接口从 IBase 接口派生/继承。

我发现如果我在项目类型为 IAdvanced 的情况下调用 DoSomething,它仍然总是返回 false。我不明白这一点。我知道,由于 IAdvanced 是 IBase 类型(因为它是该接口的子接口),它可能会导致 DoSomethingSpecific 方法的 2 种重载类型之间的混淆。但是,据我所知,我的 C# 知识有限,这里应该选择 IAdvanced 方法。这是我如何得出这个结论的一个例子:

public class Advanced: IAdvanced
{

   public void CallMethod()
   {
      DoSomething(this);
   }
}

这会产生一个真值。

但是,如果我这样做:

public class Advanced: IAdvanced
{

    public void CallMethod()
    {
       DoSomethingSpecific(this);
    }
}

它返回 false,这是我所期望的。

我不得不说我以前从未使用过泛型。虽然我尝试过,但总是陷入这样的情况,然后完全看不到使用泛型的意义(除了树和链表等数据结构)。

这次我决定来这里寻求一些建议。我正在尝试做的事情有明显的问题吗?尝试做我在这里忙着做的事情可能没有意义吗?

4

3 回答 3

4

我无法重现这一点,这意味着可能正在发生其他事情。我怀疑您实际上想说它总是返回true。这就是我在这里看到的:

using System;

public interface IBase {}
public interface IAdvanced : IBase {}

public class Base : IBase {}
public class Advanced : IAdvanced {}

class Test
{
    public static bool DoSomething<T>(T item) where T: IBase
    {
        return DoSomethingSpecific(item);
    }

    public static bool DoSomethingSpecific(IBase item)
    {
        return true;
    }

    public static bool DoSomethingSpecific(IAdvanced item)
    {
        return false;
    }

    static void Main()
    {
        Console.WriteLine(DoSomething(new Base()));     // True
        Console.WriteLine(DoSomething(new Advanced())); // True
    }    
}

现在你也写:

我知道,由于 IAdvanced 是 IBase 类型(因为它是该接口的子接口),它可能会导致 DoSomethingSpecific 方法的 2 种重载类型之间的混淆。但是,据我所知,我的 C# 知识有限,这里应该选择 IAdvanced 方法。

当然,那么您应该期望 false被退回 - 而我期望true被退回。

您会看到,其中的重载分辨率DoSomething<T>是在编译该方法时确定的。这种选择是一次性的,而不是对每个不同的人都做出一次T。因此,如果您在编译后的 IL 中查找DoSomething<T>,您只会看到对 的调用DoSomethingSpecific(IBase),而不是 DoSomethingSpecific(IAdvanced)。这里没有多态性。

至于如何“解决”这个问题 - 您需要更详细地解释您真正想要实现的目标。特别是,如果您有以下代码,您希望发生什么:

IBase x = new AdvancedImplementation();
DoSomething(x);

这里TIBase,但将引用 的实现IAdvanced。如果你想DoSomethingSpecific(IAdvanced)在这种情况下被执行,并且如果你使用的是 C# 4,那么你可以使用动态类型:

public static bool DoSomething(IBase item)
{
    dynamic dynamicItem = item;
    // Dispatch dynamically based on execution-time type
    return DoSomethingSpecific(dynamicItem);
}

请注意该方法不再需要是通用的 - 它不会带来任何好处。

另一方面,如果您想仅根据 来决定调用哪个,则T需要使用类似 ivowiblo 的解决方案。

就我个人而言,我会尽量避免这两种解决方案——我通常发现这种事情是可以通过其他方式改进的设计的症状。

于 2012-05-27T20:27:44.953 回答
2

我会放弃编译时检查并使其运行时:

public bool DoSomething(IBase item)
{
    var itemAdvanced = item as IAdvanced;
    if (itemAdvanced != null)
        return DoSomethingSpecific(itemAdvanced);
    else
        return DoSomethingSpecific(item);
}

原因是,如果你的调用者只是静态地知道对象是一个IBase,但在运行时它是一个IAdvanced,难道不应该把它当作一个IAdvanced?这可能也是做你想做的最快的方法。如果您认为有必要,我只会采用这种dynamic方法,例如因为可能有大量不同的方法。

于 2012-05-28T00:18:11.067 回答
1

据它所知,它是一个 IBase。编译器需要决定你调用的是哪种方法,这就是为什么它总是选择那个方法。

一个肮脏的技巧将是这样做:

public static bool DoSomething<T>(T item) where T: IBase
{
    var isAdvanced = typeof(IAdvanced).IsAssignableFrom(typeof(T));
    return isAdvanced ? DoSomethingSpecific((IAdvanced)item) : DoSomethingSpecific(item);
}

另一种方法是使用 Double-Dispatch/Visitor 模式:

public interface IDoSomethingVisitor {
    bool DoSomethingSpecific(IBase base);
    bool DoSomethingSpecific(IAdvanced adv);
}

DoSomething 方法将在您的 IBase 界面中:

public interface IBase{
    void DoSomething(IDoSomethingVisitor visitor);
}

在您的实现中:

public class Base : IBase
{
   public bool DoSomething(IDoSomethingVisitor visitor)
   {
      visitor.DoSomething(this);
   }
}

public class Advanced : IAdvanced
{
   public bool DoSomething(IDoSomethingVisitor visitor)
   {
      visitor.DoSomething(this);
   }
}

在这种情况下,使用纯继承解决了问题。实际实例是解析调用哪个方法的实例。没有如果。

于 2012-05-27T20:31:42.890 回答