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

本文关键字:泛型类型 重载 方法 | 更新日期: 2023-09-27 18:35:39

我有一个通用方法:

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

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

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

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

我无法重现这一点,这意味着可能正在发生其他事情。我怀疑你的意思是说它总是返回 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来决定调用哪个,则需要使用类似 ivowifblo 的解决方案。

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

我会删除编译时检查并使其运行时:

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

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

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

另一种方法是使用双重派送/访客模式:

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);
   }
}

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