【问题标题】:Use of generic types in overloaded methods在重载方法中使用泛型类型
【发布时间】:2012-05-27 20:23:23
【问题描述】:

我有一个通用方法:

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,这是我所期望的。

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

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

【问题讨论】:

    标签: c# generics overloading


    【解决方案1】:

    我无法重现这一点,这意味着可能正在发生其他事情。我怀疑您实际上 的意思是 说它总是返回 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&lt;T&gt; 中的重载分辨率是在编译该方法时确定的。该选择是一次 - 不是每个不同的T 一次。因此,如果您在编译后的 IL 中查找 DoSomething&lt;T&gt;,您将看到对 DoSomethingSpecific(IBase) 的调用,从不 DoSomethingSpecific(IAdvanced)。这里没有多态性。

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

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

    这里T 将是IBase,但 将引用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 的解决方案。

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

    【讨论】:

    • 他对 Advanced 的期望是 false
    • @ivowiblo:好吧,他声称它返回 false,但显然不是(参见我的示例)。我添加了一个编辑,解释了为什么期望它返回 false 是绝望的希望。
    • 是的,我期待的是假的。没弄错??我得到一个错误:/我将再次检查我的代码
    • 发生这种情况是因为 c# 使用静态绑定,而不是动态绑定。所以正如 Jon 所说,调用哪个方法的决定是在编译时完成的,而不是运行时
    • @Denzil:好。从中学到一件事:提供一个简短但完整的示例可以减少描述发生情况时混淆的可能性:)
    【解决方案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 方法,例如因为可能有很多不同的方法。

    【讨论】:

    • 也许这没有回答我明确提出的问题,但它肯定回答了我想问的问题。非常感谢,我会用这个。它看起来干净简单,而且我相信在性能方面不会太难。
    【解决方案3】:

    据它所知,它是一个 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);
       }
    }
    

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

    【讨论】:

    • 感谢您的建议。我会尝试这个。我担心使用 typeof 的性能,但我想我不应该预先优化
    • 我将提供一个更复杂但更好的设计
    • 您对使用双调度/访客模式的建议非常好。我已经实现了一半。我没有想过实现访问者,相反,我必须为基类的每个新派生重复更新我的接口。在我接受 Tim S 的回答后才看到这个。我想我可能更喜欢这个。我试试看!
    • 访问者工作出色。我将它应用于我的基类是抽象的情况,所以我只是将 DoSomething 方法抽象化,这迫使我为每个具体类创建 public bool DoSomething(IDoSomethingVisitor visitor)。但是,我想知道,所有方法都将完全相同(关键字“this”在每种情况下都指代其他内容)。有没有更好的办法,这样我就不用为每个具体类实现这个方法了?
    • 之所以这样,是因为除了它们看起来都一样,this 具有不同的含义,并且被调用的方法也不同。
    猜你喜欢
    • 2011-06-26
    • 2012-06-05
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 2023-03-31
    相关资源
    最近更新 更多