我是C#中扩展方法的粉丝,但是没有成功将扩展方法添加到静态类,例如Console.
例如,如果我想向Console添加一个名为'WriteBlueLine'的扩展,那么我可以去:
Console.WriteBlueLine("This text is blue");
我尝试通过添加一个本地的公共静态方法,将Console作为'this'参数...但没有骰子!
public static class Helpers { public static void WriteBlueLine(this Console c, string text) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(text); Console.ResetColor(); } }
这没有向Console添加'WriteBlueLine'方法......我做错了吗?或者要求不可能?
不可以.扩展方法需要对象的实例变量(值).但是,您可以在ConfigurationManager
界面周围编写静态包装器.如果实现了包装器,则不需要扩展方法,因为您可以直接添加方法.
public static class ConfigurationManagerWrapper { public static ConfigurationSection GetSection( string name ) { return ConfigurationManager.GetSection( name ); } ..... public static ConfigurationSection GetWidgetSection() { return GetSection( "widgets" ); } }
你能在C#中为类添加静态扩展吗?不,但你可以这样做:
public static class Extensions { public static T Create(this T @this) where T : class, new() { return Utility .Create(); } } public static class Utility where T : class, new() { static Utility() { Create = Expression.Lambda >(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile(); } public static Func Create { get; private set; } }
这是它的工作原理.虽然您无法在技术上编写静态扩展方法,但此代码利用了扩展方法中的漏洞.这个漏洞是你可以调用null对象的扩展方法而不会得到null异常(除非你通过@this访问任何东西).
所以这是你如何使用它:
var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create() // or DataSet ds2 = null; ds2 = ds2.Create(); // using some of the techniques above you could have this: (null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)
我为什么选择调用默认构造函数作为示例,为什么我不在第一个代码片段中返回新的T()而不执行所有表达式垃圾?今天是你的幸运日,因为你得到了2fer.正如任何高级.NET开发人员所知,新的T()很慢,因为它生成对System.Activator的调用,该调用在调用之前使用反射来获取默认构造函数.该死的微软!但是我的代码直接调用对象的默认构造函数.
静态扩展会比这更好,但绝望的时候需要绝望的措施.
这是不可能的.
是的,我认为MS在这里犯了一个错误.
他们的决定没有意义,迫使程序员编写(如上所述)一个毫无意义的包装类.
这是一个很好的例子:尝试扩展静态MS单元测试类断言:我想要一个更多的Assert方法AreEqual(x1,x2)
.
执行此操作的唯一方法是指向不同的类或围绕100个不同的Assert方法编写包装器.为什么!?
如果决定允许扩展实例,我认为没有合理的理由不允许静态扩展.一旦可以扩展实例,关于分割库的论点就不会出现.
在试图寻找OP所遇到的同一问题的答案时,我偶然发现了这个问题.我找不到我想要的答案,但我最终还是这样做了.
public static class MyConsole { public static void WriteLine(this ConsoleColor Color, string Text) { Console.ForegroundColor = Color; Console.WriteLine(Text); } }
我这样使用它:
ConsoleColor.Cyan.WriteLine("voilà");
也许您可以使用自定义命名空间和相同的类名添加静态类:
using CLRConsole = System.Console; namespace ExtensionMethodsDemo { public static class Console { public static void WriteLine(string value) { CLRConsole.WriteLine(value); } public static void WriteBlueLine(string value) { System.ConsoleColor currentColor = CLRConsole.ForegroundColor; CLRConsole.ForegroundColor = System.ConsoleColor.Blue; CLRConsole.WriteLine(value); CLRConsole.ForegroundColor = currentColor; } public static System.ConsoleKeyInfo ReadKey(bool intercept) { return CLRConsole.ReadKey(intercept); } } class Program { static void Main(string[] args) { try { Console.WriteBlueLine("This text is blue"); } catch (System.Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); } Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); } } }
不.扩展方法定义需要您正在扩展的类型的实例.不幸的是; 我不确定为什么需要......
从C#7开始,这不受支持.然而,有关于在C#8中集成类似内容的讨论和值得支持的提议.
对于扩展方法,扩展方法本身是静态的; 但是它们被调用就像它们是实例方法一样.由于静态类不可实例化,因此您永远不会有类的实例来调用扩展方法.因此,编译器不允许为静态类定义扩展方法.
Obnoxious先生写道:"正如任何高级.NET开发人员都知道的那样,新的T()很慢,因为它生成一个对System.Activator的调用,它在调用之前使用反射来获取默认构造函数".
如果在编译时已知类型,则将New()编译为IL"newobj"指令.Newobj采用构造函数进行直接调用.调用System.Activator.CreateInstance()编译为IL"call"指令以调用System.Activator.CreateInstance().当针对泛型类型使用New()时,将调用System.Activator.CreateInstance().Obnoxious先生的帖子在这一点上并不清楚......而且很讨厌.
这段代码:
System.Collections.ArrayList _al = new System.Collections.ArrayList(); System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));
产生这个IL:
.locals init ([0] class [mscorlib]System.Collections.ArrayList _al, [1] class [mscorlib]System.Collections.ArrayList _al2) IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() IL_0006: stloc.0 IL_0007: ldtoken [mscorlib]System.Collections.ArrayList IL_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0011: call object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type) IL_0016: castclass [mscorlib]System.Collections.ArrayList IL_001b: stloc.1
您无法向类型添加静态方法.您只能将(伪)实例方法添加到类型的实例中.
this
修饰符的要点是告诉C#编译器.
在静态/扩展方法的第一个参数的左侧传递实例.
在向类型添加静态方法的情况下,没有要为第一个参数传递的实例.