在重载方法中使用泛型类型

时间:2012-05-27 20:23:23

标签: c# generics overloading

我有一个通用的方法:

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接口派生/继承。

我发现如果我调用DoSomething,其中项目的类型为IAdvanced,它仍然总是返回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,这是我所期望的。

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

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

3 个答案:

答案 0 :(得分: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的解决方案。

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

答案 1 :(得分: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方法,例如因为可能有很多不同的方法。

答案 2 :(得分: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);
   }
}

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