如何“覆盖”.NET中的扩展方法?

时间:2013-04-22 06:15:26

标签: .net c#-3.0 extension-methods

所以我有一个对象层次结构来在asp.net mvc中生成ui控件并尝试获得流畅的api。我做了一些虚拟课,专注于当前的问题 所以这是“错误的”代码库:

public abstract class HtmlElement { /* ... */ }

public abstract class UIElement : HtmlElement { /* ... */ }

public abstract class ButtonBase : UIElement { /* ... */ }

public class LinkButton : ButtonBase { /* ... */ }

public class ActionButton : ButtonBase { /* ... */ }


public static class HtmlElementExtensions
{
  public static T Id<T>(this T item, string id) where T : HtmlElement
  {
    /* set the id */
    return item;
  }
}

public static class ButtonBaseExtensions
{
  public static T Id<T>(this T item, string id) where T : ButtonBase
  {
    /* set the id and do some button specific stuff*/
    return item;
  }
}

当我尝试在LinkBut​​ton上调用Id时,编译器说有一个模糊的调用:

LinkButton lb = new LinkButton().Id("asd");

我真的认为编译器在这种情况下选择最接近的匹配,所以如果我有一个继承自HtmlElement的Script类而不是HtmlExtensions Id方法,并且对于一个LinkBut​​ton(由于这些限制),将调用ButtonBase方法。 我有一个解决方案,但我不确定哪个更好。
我从ButtonBaseExtensions中删除了Id方法,并按以下方式修改了HtmlElementExtensions Id方法:

public static T Id<T>(this T item, string id) where T : HtmlElement
{
  if (item is ButtonBase)
  {
    /* do some button specific stuff*/
  }
  /* set the id */
  return item;
}

这样来自ButtonBase的每个类后代都在工作。 我不太喜欢我的解决方案,因为它将HtmlElement逻辑与ButtonBase逻辑混合在一起。 有什么想法/建议更好的解决方案吗? 我以为我把它们放在不同的命名空间中,但只是一秒钟。我应该使用两个命名空间,所以不解决问题。

您是否认为在msdn论坛上提及编译器是否应该关注通用扩展方法的限制?

与此同时,我在msdn论坛上进行了一些研究并开始了一个主题:link
我尝试了一些非泛型扩展方法:

  public class BaseClass { /*...*/ }
  public class InheritedClass : BaseClass { /*...*/ }

  public static class BaseClassExtensions
  {
    public static void SomeMethod(this BaseClass item, string someParameter)
    {
      Console.WriteLine(string.Format("BaseClassExtensions.SomeMethod called wtih parameter: {0}", someParameter));
    }
  }

  public static class InheritedClassExtensions
  {
    public static void SomeMethod(this InheritedClass item, string someParameter)
    {
      Console.WriteLine(string.Format("InheritedClassExtensions.SomeMethod called wtih parameter: {0}", someParameter));
    }
  }

如果我实例化这些:

BaseClass bc = new BaseClass();
InheritedClass ic = new InheritedClass();
BaseClass ic_as_bc = new InheritedClass();

bc.SomeMethod("bc");
ic.SomeMethod("ic");
ic_as_bc.SomeMethod("ic_as_bc");

制作此输出:

BaseClassExtensions.SomeMethod called wtih parameter: bc
InheritedClassExtensions.SomeMethod called wtih parameter: ic
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc

You can vote it for now

谢谢,
彼得

1 个答案:

答案 0 :(得分:2)

您可以查看有关扩展方法的MSDN文档:Extension Methods (C# Programming Guide)。有趣的部分是在编译时的绑定扩展方法

  

...它首先在类型的实例方法中查找匹配项。如果未找到匹配项,它将搜索为该类型定义的任何扩展方法,并绑定到找到的第一个扩展方法

所以这就是你看到这种行为的原因。我实际上可以买它,想象一下有人可以用一种方法public static T Id<T>(this T item, string id) where T : object覆盖你的应用程序的工作方式。如果你不会看到任何编译错误,你会认为一切都是正确的,也许一切都会有效,除了一些很少的情况。它有多混乱?

你的方法还有一个坏处。如果我将使用您的API并且会看到针对Button我有两种方法:一种在HtmlElementExtensions中,一种在ButtonBaseExtensions中,这将阻止我执行此操作HtmlElementExtensions.Id(button, "id")而不是ButtonExtensions.Id(button, "id") }?

在你的情况下,我更喜欢组合方法:

public static T Id<T>(this T item, string id) where T : HtmlElement
{
  if (item is ButtonBase)
  {
      return (T)Id((ButtonBase)item);
  } 
  else if (item is HtmlElement)
  {
      return (T)Id((HtmlElement)item);
  }

  throw new NotSupportedException("Type " + item.GetType() + " is not supported by Id extension method");
}

private static ButtonBase Id(ButtonBase item, string id)
{
     return item;
}

private static HtmlElement Id(HtmlElement item, string id)
{
     return item;
}