扩展方法与实例方法与静态类[关闭]
Posted
技术标签:
【中文标题】扩展方法与实例方法与静态类[关闭]【英文标题】:Extension Methods vs Instance Methods vs Static Class [closed] 【发布时间】:2015-08-12 17:35:45 【问题描述】:我对在 C# 中使用方法与对象进行交互的不同方式感到有些困惑,尤其是以下主要设计差异和后果:
-
调用实例方法
在 POCO 上使用静态类
创建扩展方法
例子:
public class MyPoint
public double x get; set;
public double y get; set;
public double? DistanceFrom(MyPoint p)
if (p != null)
return Math.Sqrt(Math.Pow(this.x - p.x, 2) + Math.Pow(this.y - p.y, 2));
return null;
如果您可以通过简单地在类定义中放置一个方法来实现所需的结果,那么为什么将 POCO 与静态辅助类或扩展方法结合使用会更好?
【问题讨论】:
如果你没有类定义呢?现在您需要一个静态帮助程序类或扩展方法。扩展方法允许您扩展类您不拥有其源代码。它们还允许您删除一些不太常见的函数,将它们放在您仍然可以访问的其他地方如果您需要,但默认情况下不包括在内。 @EBrown,有趣,我从来没有这样想过。 你也可以在接口上定义扩展方法。 这是扩展方法的优点之一:假设您希望通过包含方法DoSomethingCooler()
来扩展类 A
,但是类 A
的定义存在于命名空间 EBrown.NamespaceB
中单独的 DLL 你不拥有源代码,你可以定义一个扩展方法到类 A
允许你调用 A.DoSomethingCooler()
就像你拥有源代码一样。缺点:私有成员仍然是私有的。
【参考方案1】:
您问:“如果您可以通过在类定义中简单地放置一个方法来实现预期的结果,那么为什么将 POCO 与静态辅助类或扩展方法相结合会更好?”
答案是视情况而定,以及所讨论的方法是否与您班级的主要关注点直接相关(请参阅single responsibility principle)。
以下是一些示例,说明使用每种方法/方法可能是个好主意(使用您的代码示例作为起点)。
1.实例方法
//This all makes sense as instance methods because you're
//encapsulating logic MyPoint is concerned with.
public class MyPoint
public double x get; set;
public double y get; set;
public double? DistanceFrom(MyPoint p)
if (p != null)
return Math.Sqrt(Math.Pow(this.x - p.x, 2) + Math.Pow(this.y - p.y, 2));
return null;
2.静态类方法 - 错误记录示例。
//Your class doesn't directly concern itself with logging implmentation;
//that's something that is better left to a separate class, perhaps
//a "Logger" utility class with static methods that are available to your class.
public double? DistanceFrom(MyPoint p)
try
if (p != null)
return Math.Sqrt(Math.Pow(this.x - p.x, 2) + Math.Pow(this.y - p.y, 2));
return null;
catch(Exception ex)
//**** Static helper class that can be called from other classes ****
Logger.LogError(ex);
//NOTE: Logger might encapsulate other logging methods like...
//Logger.LogInformation(string s)
//...so an extension method would be less natural, since Logger
//doesn't relate to a specific base type that you can create an
//extension method for.
3.扩展方法 - XML 序列化示例。
//Maybe you want to make it so that any object can XML serialize itself
//using an easy-to-use, shared syntax.
//Your MyPoint class isn't directly concerned about XML serialization,
//so it doesn't make sense to implement this as an instance method but
//MyPoint can pick up this capability from this extension method.
public static class XmlSerialization
public static string ToXml(this object valueToSerialize)
var serializer = new XmlSerializer(valueToSerialize.GetType());
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
serializer.Serialize(writer, valueToSerialize);
return sb.ToString();
//example usage
var point = new MyPoint();
var pointXml = point.ToXml(); //<- from the extension method
经验法则是:
-
如果方法与类的主要关注点相关,请将其放入实例方法中。
如果您有一个可能对多个类有用的通用实用程序,请考虑将其放入静态类的方法中。
如果您遇到类似于 2 的情况,但与单个基类型相关,或者您认为代码看起来更简洁/无需单独引用静态类,请考虑使用扩展方法。
【讨论】:
总而言之,我认为这是最好的答案。我对您的静态类示例有疑问,但这可能只是示例本身的问题。如果有业务逻辑或与Point
本身无关的东西,那么它应该在一个单独的类中。当然,如果可能的话,它应该是一种扩展方法。像Helper
类这样的静态类被过度使用了。无论它是什么成员,它都应该在哪里(无论是在对象上还是在扩展上)。
您在ToXml
扩展方法中的第一个参数之前省略了this
。
是的,我认为静态类优于扩展方法的地方在于静态类的职责与特定类型没有直接关系。在“Logger”示例中,您可能有几个方法,例如 "Logger.LogError(Exception ex)" 和 "Logger.LogInformation(string message)"。在这种情况下,扩展方法并不适合,因为您希望 logger 封装日志记录逻辑,但没有一种自然感觉的单一类型可以扩展。
@juharr - 谢谢!我修正了遗漏。
@Colin - 带有扎实示例的绝妙答案。谢谢!【参考方案2】:
实例/静态方法
可访问成员:public
、protected
、private
(继承后无法访问)
定义:相同的类/结构/接口(可以使用partial
关键字在文件中拆分)
称为:object.Method()
在这种情况下,我的意思是静态方法是定义在它们操作的类中的方法。也就是说,它们与其他类对象一起定义。 (在您的示例代码中的 MyPoint
类中定义的静态方法。)
我们都知道(或应该知道)这些是什么以及它们的好处,除了说:
实例方法可以访问类的all private
、protected
和public
成员。 static
方法也是如此。
在大多数情况下,如果您有大量的方法和/或属性要添加,或者它们显着改变对象的操作,您应该继承原始对象(如果可能)。这使您可以访问class/struct/interface
的所有public
和protected
成员。
静态助手类方法
无障碍会员:public
定义:任何类/命名空间
称为:HelperClass.Method(object)
静态辅助类方法我暗示本节所指的static
方法的实际定义不在实际类定义中。 (即类似MyPointHelpers
或类似的类,使用您的代码示例。)
Static Helper 类方法只有可以访问对象的public
成员(很像扩展方法,我在扩展方法部分之后编写了这部分)。
静态助手类和扩展方法密切相关,并且在许多情况下是相同的。因此,我将在“扩展方法”部分中将好处留给他们。
扩展方法
无障碍会员:public
定义:任何类/命名空间
称为:object.Method()
扩展方法只有可以访问对象的public
成员。尽管他们看起来是班级的成员,但他们不是。这限制了它们的用途。 (需要访问 任何 private
或 protected
成员的方法应该不是扩展。)
在我看来,扩展方法具有三个巨大的好处。
假设您正在开发类A
,而类A
中大约有7 个方法。您还知道您想开发一些您不会总是需要的方法,但如果您这样做的话会很方便。您可以为此使用扩展方法。这些方法将被抽象到另一个类中,如果您以后需要它们,您可以在以后包含它们(按类,感谢 C# 6.0)。不常用的方法,您知道以后要使用,但您知道自己并不总是需要。
假设您正在开发程序 A
,并且您正在使用来自 DLL Something.Other.C
的类,但您不拥有该类的源代码。现在,您想添加一个与类Something.Other.C
交互的方法,该方法与实例或常规静态方法有意义,但您没有源代码所以不能!输入扩展方法,您可以在其中定义一个看似是类Something.Other.C
的成员,但实际上是您的代码的一部分的方法.
A
的定义(因为这需要更多的工作,而且除了应用程序X
之外,您不会在任何地方使用方法Y
),您可以定义一个扩展方法Y
,on 类A
,仅出现在应用程序X
中。现在,您的方法开销仅限于应用程序X
。应用程序Z
不需要这个扩展方法。
性能
就性能而言,这将取决于方法、它们的作用以及它们的作用方式。您将受制于您正在更改的对象上的任何public
属性/方法/字段,并且需要评估其性能。 (如果调用 public Value
而不是 private value
会导致一些显着的验证开销,则实例方法更有意义,因为它可以使用 private
字段或属性。)
【讨论】:
“静态助手类方法只能访问对象的公共成员” - 静态方法也可以访问定义它们的类型的私有成员。常用于工厂方法(例如SymmetricAlgorithm.Create()
、MySingleton.Instance
@Joe If static
方法是在object's class
中定义的,即。如果它们被定义,比如MyPointHelpers
,那么这是一个错误的陈述。
@Joe 我确实澄清了我在每种情况下使用术语static
时的意思。
这是一个绝妙的答案——我希望我也能接受它。非常感谢您的澄清!
@EBrown 在我被允许再次投票之前,我还有大约三个小时的时间。不过别担心 - 我会在这个问题上大肆投票。【参考方案3】:
简而言之:
-
实例方法
实例属性就像名词。例如
cat.Color = Color.Blue;
实例方法就像动词。它们应该导致与类类型相关的操作。例如cat.Meow();
这种方法在 C# 中很常见。
静态方法
将此视为辅助方法。
静态方法通常执行与类类型相关的操作...而不是特定实例。
创建类时必须定义静态方法。
示例:File.Open(String, FileMode)
是一个返回 FileStream
的静态方法。这里不需要文件的实例。
扩展方法
将此视为辅助方法。
Extension 方法是事后定义的...用于您无法更改但希望可以更改的现有第三方类。
例如:经常看到人们为DateTime
类编写扩展方法。
关于应在何时/何地使用扩展方法的争论并不少见。
参考文献
What's a “static method” in C#? File.Open【讨论】:
扩展方法也可以用来定义实例的标准行为。例如System.Linq.Enumerable 类。【参考方案4】:最大的区别是:
您可以为无法更改的对象定义扩展 实例方法可以访问私有变量,而静态方法/扩展不能静态方法和扩展基本相同:Visual Studio 允许您调用扩展方法好像它们是一个实例方法,但最终它只是一个传递实例的静态方法.
我不了解性能,我认为您不必担心太多。如果您访问大量属性,也许实例方法会更快一些,因为当前实例已经在堆栈中(但我不确定编译器是否以这种方式工作)。
我个人会在类中添加真正属于该类行为的方法,并使用扩展方法来执行范围更广的操作,例如myClass.ConvertToOtherType()
。
【讨论】:
以上是关于扩展方法与实例方法与静态类[关闭]的主要内容,如果未能解决你的问题,请参考以下文章
GroovyGroovy 扩展方法 ( 扩展静态方法示例 | 扩展实例方法示例 | 扩展实例方法与扩展静态方法代码相同 )