扩展方法的使用

13

  • 何时使用扩展方法比较合适?
  • 向类型添加扩展方法会影响性能吗?

    这些问题是关于我之前问过的扩展方法的后续问题。


  • 1
    你在这里提出了两个不同的问题;如果你将它们分开并详细说明,你可能会得到更好的答案。 - Shog9
    2
    你真的需要第二个问题吗? - John Sheehan
    我同意那一个,另一个问题应该没问题。 - Tom Anderson
    10个回答

    11

    何时使用扩展方法是有意义的?

    当你使用LINQ并希望从一个函数中输出按功能链或管道传递到另一个函数时,它就很有意义。这可以提高代码的可读性,并允许您更优雅地表达概念(无论价值如何)。

    它们还允许您在任何您喜欢的类型上给出实例方法的外观,而无需修改该类型的源代码。当合理使用时,这通常有助于提高代码的可读性和表现力。

    向类型添加扩展方法会影响性能吗?

    请注意,像这样的扩展方法调用:

    instance.SomeExtensionMethod()
    

    被编译成:

    StaticExtensionMethodClass.SomeExtensionMethod(instance);
    

    因此,性能将与任何其他静态方法调用相同。


    1
    扩展方法不仅限于与LINQ一起使用。 - Scott Dorman


    1
    扩展方法在使用函数式编程的地方发挥着重要作用。
    考虑一下你已经在应用程序中拥有的几乎所有模块级别的函数,以及当你将它们标记为扩展方法时它们变成了什么。它们成为了“主动语态”而不是“被动语态”的机会。主动语态意味着代码读起来好像一个实例提供了自己的方法来执行特定的任务,而不是函数被动地对其进行操作。
    ExtendUnlimitedCredit(AddVIP(tblCustomer, "Bill Gates"))
    

    tblCustomer.AddVIP("Bill Gates").ExtendUnlimitedCredit()
    

    扩展方法使这种转换变得简单。使用“主动语态”代码消除嵌套函数调用通常是一种改进。此外,由于我们的商店锁定了其核心类,因此我们只能在扩展方法之前利用嵌套函数调用。

    关于扩展方法的其他优点:

    1. 它们使您的函数(由于智能感知)更易发现。如果您提供了描述函数目的和用途的内联标记,智能感知甚至会为发现它的开发人员提供有用的工具提示,描述该方法及其用途(仅需按下点)。未标记为扩展方法的函数不太容易发现,可能不会被使用,结果可能会有人发明自己的版本。

    2. 虽然您实际上无法为接口实现方法,但扩展方法提供了一种替代手段,看起来您已经这样做了。


    1

    它被用于扩展(添加)现有类的功能,而不实际改变它们。

    这可以在LINQ(System.Linq命名空间和其他)如何向所有集合添加大量功能中看到。


    1

    扩展方法的另一个有趣用途是当您希望将某些功能添加到一个命名空间下的类中,而不是另一个命名空间。一个具体的例子是添加方法以便于单元测试 - 您不希望它们混杂在您的生产程序集中,但在编写单元测试时它们非常有用。


    1
    除了其他答案之外,扩展方法是向接口添加样板实现的好方法。例如,如果您想要所有列表都可以排序,则为 IList<T> 添加一个扩展方法。
    您还可以(如已经说明的那样)使用扩展方法将方法添加到您无法控制的类中;您是否曾经想过在 string 上添加一个 Reverse() 方法?加上它吧!
    唯一的区别是扩展方法不使用虚拟方法,并且没有空值检查。如果您喜欢,可以利用这一点:
    public static void ThrowIfNull<T>(this T obj, string name) where T : class
    {
        if(obj == null) throw new ArgumentNullException(name);
    }
    

    与常规的实用方法不同,它们使编写流畅界面非常容易;这是它们存在的原因之一 - 即使用LINQ:
    var foo = source.Where(predicate).OrderBy(selector);
    

    比起以下的代码,这段代码更易读:

    var foo = Enumerable.OrderBy(Enumerable.Where(source,predicate),selector);
    

    使用常规方法,要使用第一种方法,它必须是常规实例方法,这将需要更改(例如)IEnumerable<T> - 这是不可取的。


    0
    /// <summary>
    /// External Library Code
    /// </summary>
    namespace ExternalLibrary
    {
        public class Calculator
        {
            public int Number1 { get; set; }
            public int Number2 { get; set; }
    
            public int Addition()
            {
                return Number1 + Number2;
            }
    
            public int Subtraction()
            {
                return Number1 - Number2;
            }
        }
    }
    
    --------------------------------------------------------------------------------------
    
    using ExternalLibrary;
    using System;
    
    namespace StackOverFlow
    {
        class Program
        {
            static void Main(string[] args)
            {
                Calculator calc = new Calculator()
                {
                    Number1 = 5,
                    Number2 = 3
                };
                Console.WriteLine(calc.Addition());
                Console.WriteLine(calc.Subtraction());
    
                // Here we want multiplication also. but we don't have access Calculator
                // class code, so we can't modify in that.
                // In order to achieve this functionality we can use extension method.
                Console.WriteLine(calc.Multiplication());
    
                Console.ReadLine();
            }
        }
    
        /// <summary>
        /// Extension Method for multiplication
        /// </summary>
        public static class CalculatorExtension
        {
            public static int Multiplication(this Calculator calc)
            {
                return calc.Number1 * calc.Number2;
            }
        }
    }
    

    0

    我不知道有任何性能影响。当您无法访问源代码并且因此无法直接将方法添加到类中时,使用扩展方法是最合理的选择,并且该方法可以作为函数实现。这涉及到我在您之前的问题中发表的评论,在那里另一个人给出了一个关于“string”类的扩展方法示例,该示例基于字符串是否为有效电子邮件返回布尔值。在我看来,这是不应该使用扩展方法的示例,因为该函数对于字符串类型并不基本。但是,向“string”添加Left(int)和Right(int)函数确实是有意义的。


    0
    回答你的第二个问题:我的经验法则是,扩展方法应该是类型的“自然功能扩展”,或者它应该是流畅接口中可维护和可读的一部分。
    一个“自然功能扩展”的例子是将数据读取器扩展为在遇到 DBNull 时返回默认值。不太自然的是将数据读取器扩展为返回由多个字段中的数据表示的实体的实例。在后一种情况下,你正在向对象注入错误的责任 :)。

    0

    我使用它们来重用我的对象模型类。我有一堆代表数据库中对象的类。这些类仅在客户端用于显示对象,因此基本用法是访问属性。

    public class Stock {
       public Code { get; private set; }
       public Name { get; private set; }
    }
    

    由于这种使用模式,我不想在这些类中拥有业务逻辑方法,因此我将每个业务逻辑都作为扩展方法。

    public static class StockExtender {
        public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
        {...}
    }
    

    这样我就可以使用相同的类来处理业务逻辑和用户界面显示,而不会在客户端上加载不必要的代码。

    这个解决方案有趣的地方在于,我的对象模型类是使用Mono.Cecil动态生成的,因此即使我想添加业务逻辑方法也非常困难。我有一个编译器,它读取XML定义文件并生成这些存储在数据库中的对象的存根类。在这种情况下,唯一的方法是扩展它们。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接