Pét*_*ter 7 .net extension-methods c#-3.0
所以我有一个对象层次结构来在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;
}
}
Run Code Online (Sandbox Code Playgroud)
当我尝试在LinkButton上调用Id时,编译器说有一个模糊的调用:
LinkButton lb = new LinkButton().Id("asd");
Run Code Online (Sandbox Code Playgroud)
我真的认为编译器在这种情况下选择最接近的匹配,所以如果我有一个继承自HtmlElement的Script类而不是HtmlExtensions Id方法,并且对于一个LinkButton(由于这些限制),将调用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;
}
Run Code Online (Sandbox Code Playgroud)
这样,ButtonBase的每个类后代都在工作.我不太喜欢我的解决方案,因为它将HtmlElement逻辑与ButtonBase逻辑混合在一起.有什么想法/建议更好的解决方案吗?我以为我把它们放在不同的命名空间中,但只是一秒钟.我应该使用两个命名空间,所以不解决问题.
您是否认为在msdn论坛上值得一提的是编译器应该注意对通用扩展方法的限制?
与此同时,我在msdn论坛上做了一些研究并启动了一个帖子:链接
我尝试了一些非泛型的扩展方法:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
如果我实例化这些:
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");
Run Code Online (Sandbox Code Playgroud)
产生了这个输出:
BaseClassExtensions.SomeMethod called wtih parameter: bc
InheritedClassExtensions.SomeMethod called wtih parameter: ic
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc
Run Code Online (Sandbox Code Playgroud)
谢谢,
Péter
你可以看看MSDN关于扩展方法的文档:扩展方法(C#编程指南)。有趣的部分是在编译时绑定扩展方法:
...它首先在类型的实例方法中查找匹配项。如果未找到匹配项,它将搜索为该类型定义的任何扩展方法,并绑定到它找到的第一个扩展方法。
这就是您看到这种行为的原因。我实际上可以购买它,想象一下有人可以仅用一种方法覆盖您的应用程序的工作方式public static T Id<T>(this T item, string id) where T : object。如果你不会看到任何编译器错误,你会认为一切都是正确的,也许一切都会工作,除了一些罕见的情况。这有多令人困惑?
你的方法还有一件不好的事情。如果我将使用您的 API 并会看到对于 Button 我有两种方法:一种 inHtmlElementExtensions和一种 in ,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;
}
Run Code Online (Sandbox Code Playgroud)