扩展方法与实例方法与静态方法 Class

Extension Methods vs Instance Methods vs Static Class

我对在 C# 中使用方法与对象交互的不同方式感到有点困惑,尤其是以下主要设计差异和结果:

  1. 调用实例方法
  2. 在 POCO
  3. 上使用静态 class
  4. 创建扩展方法

示例:

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().

简而言之:

  1. 实例方法
    • 实例属性就像名词。例如cat.Color = Color.Blue;
    • 实例方法就像动词。它们应该导致与 class 类型相关的操作。例如cat.Meow();
    • 这种方法在 C# 中很常见。
  2. 静态方法
    • 将此视为辅助方法。
    • 静态方法通常执行与 class 类型相关的操作...而不是特定实例。
    • 创建 class.
    • 时必须定义静态方法
    • 示例:File.Open(String, FileMode)是一个静态方法,即returns一个FileStream。这里不需要 file 的实例。
  3. 扩展方法
    • 将此视为辅助方法。
    • Extension 方法是事后定义的...对于现有的第三方 classes 你不能改变,但希望你可以。
    • 例如:看到人们为 DateTime class.
    • 编写扩展方法并不少见
    • 关于 when/where 应该使用扩展方法的争论并不少见。

参考资料

  • What's a “static method” in C#?
  • File.Open

Instance/Static 方法

可访问成员publicprotectedprivate(继承则不可访问)

定义:相同class/struct/interface(可以使用partial关键字跨文件拆分)

调用为object.Method()

在这种情况下,我的意思是 静态方法 是在它们操作的 class 中定义 的方法。 也就是说,它们与其他 class 对象一起定义。 (在示例代码的 MyPoint class 中定义的静态方法。)

我们都知道(或应该知道)这些是什么以及对它们有什么好处,我就不多说了,只说:

实例方法可以访问 所有 privateprotected 和 class 的 public 成员。 static 方法也是如此。

在大多数情况下,如果您有大量方法and/or属性要添加,或者它们显着改变对象的操作,您应该继承 原始对象(如果可能)。这使您可以访问 class/struct/interface.

的所有 publicprotected 成员

静态助手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 的成员,但他们不是。这限制了它们的用途。 (需要访问 privateprotected 成员的 any 的方法应该 而不是 是扩展。)

在我看来,扩展方法具有三个巨大 好处。

  1. 假设你在开发classA,classA里面大概有7个方法。您还知道您想开发一些您不会 总是 需要的方法,但如果您这样做的话,那会很方便。您可以为此使用扩展方法。这些方法将在另一个 class 中抽象出来,如果您需要它们,您可以在以后包含(通过 class,感谢 C# 6.0)。您知道以后要使用的不常用方法,但您知道您并不总是需要。

  2. 假设您正在开发程序 A,并且您正在使用来自 DLL Something.Other.C 的 class,您不拥有该程序的源代码。现在,您想添加一个与 class Something.Other.C 交互的方法,其方式 对实例或常规静态方法有意义 ,但您还没有没有来源,所以你不能!输入扩展方法,您可以在其中定义一个方法,该方法看起来是classSomething.Other.C的成员,但实际上是 ] 你的代码的一部分。

  3. 假设您开发了自己的库,并在很多自己的应用程序中使用,并且您在开发应用程序的过程中意识到 X 您确实可以使用一种方法 Y 在 class A 上再次。好吧,与其修改 class A 的定义(因为那需要更多的工作,而且除了应用程序 X 之外,您不会在任何地方使用方法 Y),您可以定义扩展方法 Y, on class Aonly 存在于应用程序 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

经验法则是:

  1. 如果该方法涉及 class' 主要问题,请将其放在实例方法中。
  2. 如果您有一个可能对多个 class 有用的通用实用程序,请考虑将其放入静态 class' 方法中。
  3. 如果您遇到类似于 2 的情况,但与单个基类型相关,或者您认为代码看起来 cleaner/more 简洁而无需单独引用静态 class,请考虑扩展方法。