我可以将扩展方法添加到现有的静态类吗?

我是C#中扩展方法的粉丝,但没有成功添加扩展方法到静态类,如控制台。

例如,如果我想向控制台添加一个名为“WriteBlueLine”的扩展,那么我可以这样做:

Console.WriteBlueLine("This text is blue"); 

我试图通过添加一个本地的公共静态方法,作为一个“这个”参数的控制台…但没有骰子!

 public static class Helpers { public static void WriteBlueLine(this Console c, string text) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(text); Console.ResetColor(); } } 

这没有添加一个'WriteBlueLine'方法到控制台…我做错了吗? 或者要求不可能的?

不。扩展方法需要一个对象的实例variables(值)。 但是,您可以在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<T>(this T @this) where T : class, new() { return Utility<T>.Create(); } } public static class Utility<T> where T : class, new() { static Utility() { Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile(); } public static Func<T> Create { get; private set; } } 

这是如何工作的。 虽然你不能在技术上编写静态的扩展方法,但是这个代码却在扩展方法中占据了一个漏洞。 这个漏洞是你可以调用空对象的扩展方法,而不会得到空的exception(除非你通过@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(...) 

现在为什么我select调用默认构造函数作为示例,AND为什么不在第一个代码片段中返回新的T(),而没有执行所有的expression式垃圾? 今天你的幸运日,因为你得到了一个2fer。 正如任何高级的.NET开发人员所知道的,新的T()很慢,因为它会调用System.Activator,在调用它之前使用reflection来获取默认的构造函数。 该死的你微软! 不过,我的代码直接调用对象的默认构造函数。

静态的扩展会比这更好,但绝望的时候需要绝望的措施。

这是不可能的。

是的,我认为MS在这里犯了一个错误。

他们的决定是没有意义的,并迫使程序员写(如上所述)一个毫无意义的包装类。

下面是一个很好的例子:尝试扩展静态MSunit testing类断言:我想要更多的断言方法AreEqual(x1,x2)

唯一的方法就是指向不同的类,或者写一个包装100个左右不同的Assert方法。 为什么!?

如果决定允许实例的扩展,我认为不允许静态扩展的逻辑理由。 一旦实例可以被扩展,关于切片库的观点就不会成立。

也许你可以添加一个静态类与您的自定义名称空间和相同的类名称:

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

不。 扩展方法定义需要您正在扩展的types的一个实例。 它的不幸; 我不知道为什么要求…

至于扩展方法,扩展方法本身是静态的; 但它们被调用,就好像它们是实例方法一样。 由于静态类不是可实例化的,因此永远不会有类的实例从中调用扩展方法。 为此,编译器不允许为静态类定义扩展方法。

Obnoxious先生写道:“正如任何先进的.NET开发人员所知道的,新的T()很慢,因为它调用System.Activator,在调用它之前使用reflection来获取默认的构造函数”。

如果types在编译时已知,则New()被编译为IL“newobj”指令。 Newobj需要一个构造函数来直接调用。 调用System.Activator.CreateInstance()编译为IL“调用”指令来调用System.Activator.CreateInstance()。 对genericstypes使用New()将导致对System.Activator.CreateInstance()的调用。 Obnoxious先生的这个post在这一点上还不清楚,而且很讨厌。

此代码:

 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 

您不能将静态方法添加到types。 您只能将(伪)实例方法添加到types的实例。

this修饰符的要点是告诉C#编译器通过左边的实例. 作为静态/扩展方法的第一个参数。

在向types添加静态方法的情况下,没有实例传递第一个参数。

我偶然发现这个线程,同时试图findOP的同一个问题的答案。 我没有find我想要的答案,但我最终这样做了。

 public static class MyConsole { public static void WriteLine(this ConsoleColor Color, string Text) { Console.ForegroundColor = Color; Console.WriteLine(Text); } } 

我这样使用它:

 ConsoleColor.Cyan.WriteLine("voilà"); 

当我学习扩展方法并且不成功时,我试图用System.Environment做这个。 正如其他人所说,原因是因为扩展方法需要类的一个实例。

是的,在有限的意义上。

 public class DataSet : System.Data.DataSet { public static void SpecialMethod() { } } 

这工作,但控制台不是因为它是静态的。

 public static class Console { public static void WriteLine(String x) { System.Console.WriteLine(x); } public static void WriteBlueLine(String x) { System.Console.ForegroundColor = ConsoleColor.Blue; System.Console.Write(.x); } } 

这工作,因为只要它不在同一个命名空间。 问题是你必须为System.Console拥有的每个方法编写一个代理静态方法。 这不一定是坏事,因为你可以添加这样的东西:

  public static void WriteLine(String x) { System.Console.WriteLine(x.Replace("Fck","****")); } 

要么

  public static void WriteLine(String x) { System.Console.ForegroundColor = ConsoleColor.Blue; System.Console.WriteLine(x); } 

它的工作方式是将某些东西挂钩到标准的WriteLine中。 这可能是一个行数或坏词filter或其他。 无论何时只要在命名空间中指定控制台(如WebProject1)并导入命名空间System,就会将WebProject1.Console选为System.Console作为命名空间WebProject1中这些类的默认值。 所以这个代码将把所有的Console.WriteLine调用变成蓝色,只要你从未指定System.Console.WriteLine。

以下被拒绝作为tvanfosson答案的编辑 。 我被要求贡献它作为我自己的答案。 我用他的build议,并完成了一个ConfigurationManager包装的实现。 原则上,我只是填写...在tvanfosson的答案。

不需要。扩展方法需要一个对象的实例。 但是,您可以在ConfigurationManager界面上编写一个静态包装器。 如果你实现了包装,你不需要扩展方法,因为你可以直接添加方法。

 public static class ConfigurationManagerWrapper { public static NameValueCollection AppSettings { get { return ConfigurationManager.AppSettings; } } public static ConnectionStringSettingsCollection ConnectionStrings { get { return ConfigurationManager.ConnectionStrings; } } public static object GetSection(string sectionName) { return ConfigurationManager.GetSection(sectionName); } public static Configuration OpenExeConfiguration(string exePath) { return ConfigurationManager.OpenExeConfiguration(exePath); } public static Configuration OpenMachineConfiguration() { return ConfigurationManager.OpenMachineConfiguration(); } public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel) { return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel); } public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap) { return ConfigurationManager.OpenMappedMachineConfiguration(fileMap); } public static void RefreshSection(string sectionName) { ConfigurationManager.RefreshSection(sectionName); } } 

从C#7开始,这不受支持。 然而,有关于将C#8中的东西整合进来的讨论和值得支持的build议 。

你可以在null上使用强制转换来使其工作。

 public static class YoutTypeExtensionExample { public static void Example() { ((YourType)null).ExtensionMethod(); } } 

扩展名:

 public static class YourTypeExtension { public static void ExtensionMethod(this YourType x) { } } 

YourType:

 public class YourType { } 

你可以这样做,如果你愿意通过创build一个静态类的variables并将其赋值为null来“修饰”它。 但是,这个方法对于类的静态调用是不可用的,所以不知道它有多less用处:

 Console myConsole = null; myConsole.WriteBlueLine("my blue line"); public static class Helpers { public static void WriteBlueLine(this Console c, string text) { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine(text); Console.ResetColor(); } }