17

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

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

例子:

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 与静态帮助程序类或扩展方法结合使用会更好呢?

4

4 回答 4

20

您问,“如果您可以通过简单地将方法放在类定义中来实现预期的结果,为什么 POCO 与静态帮助程序类或扩展方法相结合更可取?”

答案是视情况而定,以及所讨论的方法是否与您的班级的主要关注点直接相关(请参阅单一责任原则)。

以下是一些示例,说明使用每种方法/方法可能是一个好主意(使用您的代码示例作为起点)。

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

经验法则是:

  1. 如果该方法与类的主要关注点相关,请将其放在实例方法中。
  2. 如果您有一个可能对多个类有用的通用实用程序,请考虑将其放入静态类的方法中。
  3. 如果您遇到类似于 2 的情况,但与单个基类型相关,或者您认为代码看起来更简洁/更简洁而无需单独引用静态类,请考虑使用扩展方法。
于 2015-05-29T18:52:19.863 回答
14

实例/静态方法

可访问成员: public, protected, private(如果继承则无法访问)

定义:相同的类/结构/接口(可以使用partial关键字在文件中拆分)

称为object.Method()

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

我们都知道(或应该知道)这些是什么以及它们的好处,我不会详细说明,只是说:

实例方法可以访问类的所有 privateprotectedpublic成员。方法也一样static

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

静态助手类方法

无障碍会员public

定义:任何类/命名空间

称为HelperClass.Method(object)

通过静态助手类方法,我暗示static本节提到的方法的实际定义不在实际的类定义中。(即类似MyPointHelpers或类似的类,使用您的代码示例。)

静态 Helper 类方法只能访问public对象的成员(很像扩展方法,我在扩展方法部分之后编写了这部分)。

静态 Helper 类和扩展方法密切相关,并且在许多情况下是相同的。因此,我将在“扩展方法”部分中将这些好处留给他们。

扩展方法

无障碍会员public

定义:任何类/命名空间

称为object.Method()

扩展方法只能访问public对象的成员。虽然他们看起来是班级的成员,但他们不是。这限制了它们的用途。(需要访问任何privateprotected成员的方法不应是扩展。)

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

  1. 假设您正在开发 class A,并且 classA中有大约 7 个方法。你也知道你想开发一些你并不总是需要的方法,但如果你这样做的话会很方便。您可以为此使用扩展方法。这些方法将被抽象到另一个类中,如果您以后需要它们,您可以在以后包含它们(按类,感谢 C# 6.0)。您知道以后要使用的不常见方法,但您知道您并不总是需要。

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

  3. 假设您开发了自己的库,并与很多您自己的应用程序一起使用,并且您在开发应用程序的过程中意识到您真的可以再次在类上X使用方法。好吧,您可以在仅存在于application中的类定义扩展方法,而不是修改 class 的定义(因为这需要更多工作,而且您不会在任何地方使用方法 application ) 。现在,您的方法开销仅限于 application 。应用程序不需要有这个扩展方法。YAAYXYAXXZ

表现

就性能而言,这将取决于方法、他们做什么以及他们是如何做到的。您将受制于public您正在更改的对象上的任何属性/方法/字段,并且需要评估其性能。(如果调用public Value而不是private value导致一些显着的验证开销,实例方法更有意义,因为它可以使用private字段或属性。)

于 2015-05-29T18:42:51.960 回答
7

简而言之:

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

参考

于 2015-05-29T18:33:30.063 回答
1

最大的区别是:

  • 您可以定义无法更改的对象的扩展
  • 实例方法可以访问私有变量,而静态方法/扩展不能

静态方法和扩展基本相同:Visual Studio 允许您像调用实例方法一样调用扩展方法,但最终它只是一个传递实例的静态方法。

我不知道性能,我认为你不应该担心太多。如果您访问大量属性,也许实例方法会更快一些,因为当前实例已经在堆栈上(但我不确定编译器是否以这种方式工作)。

就我个人而言,如果它们确实属于该类的行为,我会向类添加方法,并使用扩展方法来执行范围更广的操作,例如myClass.ConvertToOtherType().

于 2015-05-29T18:30:06.103 回答