扩展方法与实例方法与静态方法 Class
Extension Methods vs Instance Methods vs Static Class
我对在 C# 中使用方法与对象交互的不同方式感到有点困惑,尤其是以下主要设计差异和结果:
- 调用实例方法
- 在 POCO
上使用静态 class
- 创建扩展方法
示例:
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;
}
}
如果您可以通过简单地在 class 定义中放置一个方法来实现预期的结果,为什么 POCO 与静态助手 class 或扩展方法相结合会更可取?
最大的区别是:
- 您可以为无法更改的对象定义扩展
- 实例方法可以访问私有变量,而静态methods/extensions不能
静态方法和扩展基本相同:Visual Studio允许你调用扩展方法就好像它们是一个实例方法,但最终它只是一个传递给它的实例的静态方法。
我不了解性能,我认为您不必太担心。如果您访问大量属性,实例方法可能会更快一些,因为当前实例已经在堆栈中(但我不确定编译器是否以这种方式工作)。
我个人将方法添加到 classes,如果它们确实属于 class 的行为,并使用扩展方法来执行具有更广泛范围的操作,例如 myClass.ConvertToOtherType()
.
简而言之:
- 实例方法
- 实例属性就像名词。例如
cat.Color = Color.Blue;
- 实例方法就像动词。它们应该导致与 class 类型相关的操作。例如
cat.Meow();
- 这种方法在 C# 中很常见。
- 静态方法
- 将此视为辅助方法。
- 静态方法通常执行与 class 类型相关的操作...而不是特定实例。
- 创建 class.
时必须定义静态方法
- 示例:
File.Open(String, FileMode)
是一个静态方法,即returns一个FileStream
。这里不需要 file 的实例。
- 扩展方法
- 将此视为辅助方法。
- Extension 方法是事后定义的...对于现有的第三方 classes 你不能改变,但希望你可以。
- 例如:看到人们为
DateTime
class. 编写扩展方法并不少见
- 关于 when/where 应该使用扩展方法的争论并不少见。
参考资料
- What's a “static method” in C#?
- File.Open
Instance/Static 方法
可访问成员:public
、protected
、private
(继承则不可访问)
定义:相同class/struct/interface(可以使用partial
关键字跨文件拆分)
调用为:object.Method()
在这种情况下,我的意思是 静态方法 是在它们操作的 class 中定义 的方法。 也就是说,它们与其他 class 对象一起定义。 (在示例代码的 MyPoint
class 中定义的静态方法。)
我们都知道(或应该知道)这些是什么以及对它们有什么好处,我就不多说了,只说:
实例方法可以访问 所有 private
、protected
和 class 的 public
成员。 static
方法也是如此。
在大多数情况下,如果您有大量方法and/or属性要添加,或者它们显着改变对象的操作,您应该继承 原始对象(如果可能)。这使您可以访问 class/struct/interface
.
的所有 public
和 protected
成员
静态助手Class 方法
可访问成员:public
定义:任意class/namespace
调用为: HelperClass.Method(object)
通过 Static Helper Class 方法 我的意思是本节所指的 static
方法的实际定义是 not 在实际的 class 定义中。 (即 class 类似 MyPointHelpers
或类似的,使用您的代码示例。)
Static Helper class 方法 只有 可以访问对象的 public
成员(很像扩展方法,我在扩展之后写了这一部分方法部分)。
静态助手 classes 和扩展方法密切相关,并且在许多情况下是相同的。因此,我将在扩展方法部分将好处留给他们。
扩展方法
可访问成员:public
定义:任意class/namespace
调用为:object.Method()
扩展方法仅 可以访问对象的public
成员。尽管他们看起来 是class 的成员,但他们不是。这限制了它们的用途。 (需要访问 private
或 protected
成员的 any 的方法应该 而不是 是扩展。)
在我看来,扩展方法具有三个巨大 好处。
假设你在开发classA
,classA
里面大概有7个方法。您还知道您想开发一些您不会 总是 需要的方法,但如果您这样做的话,那会很方便。您可以为此使用扩展方法。这些方法将在另一个 class 中抽象出来,如果您需要它们,您可以在以后包含(通过 class,感谢 C# 6.0)。您知道以后要使用的不常用方法,但您知道您并不总是需要。
假设您正在开发程序 A
,并且您正在使用来自 DLL Something.Other.C
的 class,您不拥有该程序的源代码。现在,您想添加一个与 class Something.Other.C
交互的方法,其方式 对实例或常规静态方法有意义 ,但您还没有没有来源,所以你不能!输入扩展方法,您可以在其中定义一个方法,该方法看起来是classSomething.Other.C
的成员,但实际上是 ] 你的代码的一部分。
假设您开发了自己的库,并在很多自己的应用程序中使用,并且您在开发应用程序的过程中意识到 X
您确实可以使用一种方法 Y
在 class A
上再次。好吧,与其修改 class A
的定义(因为那需要更多的工作,而且除了应用程序 X
之外,您不会在任何地方使用方法 Y
),您可以定义扩展方法 Y
, on class A
即 only 存在于应用程序 X
.现在,您的方法开销严格限制在应用程序 X
内。应用程序Z
不需要有这个扩展方法。
性能
就性能而言,这取决于方法、它们做什么以及它们是如何做的。您将受到您正在更改的对象上的任何 public
properties/methods/fields 的影响,并且需要衡量其性能。 (如果调用 public Value
而不是 private value
会导致一些显着的验证开销,则实例方法更有意义,因为它可以使用 private
字段或属性。)
你问了,"If you could accomplish the desired outcome by simply placing a method in a class definition, why would a POCO combined with either a static helper class or an extension method be preferable?"
答案是,这取决于具体情况,如果所讨论的方法与您 class 的主要关注点直接相关(请参阅 single responsibility principle)。
这里有一些例子,说明在哪些地方使用每种类型的 approach/method 可能是个好主意(使用您的代码示例作为起点)。
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。静态 Class 方法 - 错误记录示例。
//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
经验法则是:
- 如果该方法涉及 class' 主要问题,请将其放在实例方法中。
- 如果您有一个可能对多个 class 有用的通用实用程序,请考虑将其放入静态 class' 方法中。
- 如果您遇到类似于 2 的情况,但与单个基类型相关,或者您认为代码看起来 cleaner/more 简洁而无需单独引用静态 class,请考虑扩展方法。
我对在 C# 中使用方法与对象交互的不同方式感到有点困惑,尤其是以下主要设计差异和结果:
- 调用实例方法
- 在 POCO 上使用静态 class
- 创建扩展方法
示例:
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;
}
}
如果您可以通过简单地在 class 定义中放置一个方法来实现预期的结果,为什么 POCO 与静态助手 class 或扩展方法相结合会更可取?
最大的区别是:
- 您可以为无法更改的对象定义扩展
- 实例方法可以访问私有变量,而静态methods/extensions不能
静态方法和扩展基本相同:Visual Studio允许你调用扩展方法就好像它们是一个实例方法,但最终它只是一个传递给它的实例的静态方法。
我不了解性能,我认为您不必太担心。如果您访问大量属性,实例方法可能会更快一些,因为当前实例已经在堆栈中(但我不确定编译器是否以这种方式工作)。
我个人将方法添加到 classes,如果它们确实属于 class 的行为,并使用扩展方法来执行具有更广泛范围的操作,例如 myClass.ConvertToOtherType()
.
简而言之:
- 实例方法
- 实例属性就像名词。例如
cat.Color = Color.Blue;
- 实例方法就像动词。它们应该导致与 class 类型相关的操作。例如
cat.Meow();
- 这种方法在 C# 中很常见。
- 实例属性就像名词。例如
- 静态方法
- 将此视为辅助方法。
- 静态方法通常执行与 class 类型相关的操作...而不是特定实例。
- 创建 class. 时必须定义静态方法
- 示例:
File.Open(String, FileMode)
是一个静态方法,即returns一个FileStream
。这里不需要 file 的实例。
- 扩展方法
- 将此视为辅助方法。
- Extension 方法是事后定义的...对于现有的第三方 classes 你不能改变,但希望你可以。
- 例如:看到人们为
DateTime
class. 编写扩展方法并不少见
- 关于 when/where 应该使用扩展方法的争论并不少见。
参考资料
- What's a “static method” in C#?
- File.Open
Instance/Static 方法
可访问成员:public
、protected
、private
(继承则不可访问)
定义:相同class/struct/interface(可以使用partial
关键字跨文件拆分)
调用为:object.Method()
在这种情况下,我的意思是 静态方法 是在它们操作的 class 中定义 的方法。 也就是说,它们与其他 class 对象一起定义。 (在示例代码的 MyPoint
class 中定义的静态方法。)
我们都知道(或应该知道)这些是什么以及对它们有什么好处,我就不多说了,只说:
实例方法可以访问 所有 private
、protected
和 class 的 public
成员。 static
方法也是如此。
在大多数情况下,如果您有大量方法and/or属性要添加,或者它们显着改变对象的操作,您应该继承 原始对象(如果可能)。这使您可以访问 class/struct/interface
.
public
和 protected
成员
静态助手Class 方法
可访问成员:public
定义:任意class/namespace
调用为: HelperClass.Method(object)
通过 Static Helper Class 方法 我的意思是本节所指的 static
方法的实际定义是 not 在实际的 class 定义中。 (即 class 类似 MyPointHelpers
或类似的,使用您的代码示例。)
Static Helper class 方法 只有 可以访问对象的 public
成员(很像扩展方法,我在扩展之后写了这一部分方法部分)。
静态助手 classes 和扩展方法密切相关,并且在许多情况下是相同的。因此,我将在扩展方法部分将好处留给他们。
扩展方法
可访问成员:public
定义:任意class/namespace
调用为:object.Method()
扩展方法仅 可以访问对象的public
成员。尽管他们看起来 是class 的成员,但他们不是。这限制了它们的用途。 (需要访问 private
或 protected
成员的 any 的方法应该 而不是 是扩展。)
在我看来,扩展方法具有三个巨大 好处。
假设你在开发class
A
,classA
里面大概有7个方法。您还知道您想开发一些您不会 总是 需要的方法,但如果您这样做的话,那会很方便。您可以为此使用扩展方法。这些方法将在另一个 class 中抽象出来,如果您需要它们,您可以在以后包含(通过 class,感谢 C# 6.0)。您知道以后要使用的不常用方法,但您知道您并不总是需要。假设您正在开发程序
A
,并且您正在使用来自 DLLSomething.Other.C
的 class,您不拥有该程序的源代码。现在,您想添加一个与 classSomething.Other.C
交互的方法,其方式 对实例或常规静态方法有意义 ,但您还没有没有来源,所以你不能!输入扩展方法,您可以在其中定义一个方法,该方法看起来是classSomething.Other.C
的成员,但实际上是 ] 你的代码的一部分。假设您开发了自己的库,并在很多自己的应用程序中使用,并且您在开发应用程序的过程中意识到
X
您确实可以使用一种方法Y
在 classA
上再次。好吧,与其修改 classA
的定义(因为那需要更多的工作,而且除了应用程序X
之外,您不会在任何地方使用方法Y
),您可以定义扩展方法Y
, on classA
即 only 存在于应用程序X
.现在,您的方法开销严格限制在应用程序X
内。应用程序Z
不需要有这个扩展方法。
性能
就性能而言,这取决于方法、它们做什么以及它们是如何做的。您将受到您正在更改的对象上的任何 public
properties/methods/fields 的影响,并且需要衡量其性能。 (如果调用 public Value
而不是 private value
会导致一些显着的验证开销,则实例方法更有意义,因为它可以使用 private
字段或属性。)
你问了,"If you could accomplish the desired outcome by simply placing a method in a class definition, why would a POCO combined with either a static helper class or an extension method be preferable?"
答案是,这取决于具体情况,如果所讨论的方法与您 class 的主要关注点直接相关(请参阅 single responsibility principle)。
这里有一些例子,说明在哪些地方使用每种类型的 approach/method 可能是个好主意(使用您的代码示例作为起点)。
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。静态 Class 方法 - 错误记录示例。
//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
经验法则是:
- 如果该方法涉及 class' 主要问题,请将其放在实例方法中。
- 如果您有一个可能对多个 class 有用的通用实用程序,请考虑将其放入静态 class' 方法中。
- 如果您遇到类似于 2 的情况,但与单个基类型相关,或者您认为代码看起来 cleaner/more 简洁而无需单独引用静态 class,请考虑扩展方法。