【问题标题】:Usage of Extension Methods扩展方法的使用
【发布时间】:2008-12-31 17:52:48
【问题描述】:

  • 什么时候使用扩展方法才有意义?
  • 向类型添加扩展方法会影响性能吗?

    这些问题是我之前提出的关于 Extension Methods 的问题的后续问题。

  • 【问题讨论】:

    • 你在这里问了两个不同的问题;如果您将它们拆分并扩展它们,您可能会得到更好的答案。
    • 你真的需要第二个问题吗??
    • 我同意那个,另一个问题应该没问题

    标签: .net extension-methods


    【解决方案1】:

    什么时候使用扩展方法有意义?

    当您使用 LINQ 并希望将函数输出从一个函数链接或管道传输到另一个函数时,它们很有意义。它提高了代码的可读性,并允许您更优雅地表达一个概念(无论它是否有价值)。

    它们还允许您在不修改该类型的源代码的情况下为您喜欢的任何类型提供实例方法的外观,这通常有助于合理使用代码的可读性和表现力

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

    请注意,扩展方法调用如下:

    instance.SomeExtensionMethod()
    

    编译为:

    StaticExtensionMethodClass.SomeExtensionMethod(instance);
    

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

    【讨论】:

    • 扩展方法不限于仅与 LINQ 一起使用。
    【解决方案2】:

    来自我的回答here

    关于扩展方法的实际用途,您可以在不派生新类的情况下向类添加新方法。

    看看下面的例子:

    public class extended {
        public int sum() {
            return 7+3+2;
        }
    }
    
    public static class extending {
        public static float average(this extended extnd) {
            return extnd.sum() / 3;
        }
    }
    

    如您所见,Extending 类正在向Extended 类添加一个名为 average 的方法。要获得平均值,请调用 average 方法,因为它属于 extended 类:

    extended ex = new extended();
    
    Console.WriteLine(ex.average());
    

    参考:http://aspguy.wordpress.com/2008/07/03/a-practical-use-of-serialization-and-extension-methods-in-c-30/


    关于性能,我认为您可以看到扩展方法的改进,因为它们永远不会动态调度,但这完全取决于动态方法的实现方式。

    【讨论】:

      【解决方案3】:

      它用于扩展(添加)现有类的功能,而无需实际更改它们。

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          除了其他答案之外,扩展方法是将样板实现添加到接口的好方法。例如,如果您希望所有列表都可排序,请为 IList<T> 添加扩展方法。

          您也可以(如前所述)使用扩展方法将方法添加到您无法控制的类中;曾经想要string 上的Reverse() 方法吗?添加一个!

          唯一的区别是扩展方法不使用virtual,并且没有null-check。如果您愿意,可以利用它来发挥自己的优势:

          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&lt;T&gt; - 不可取。

          【讨论】:

            【解决方案6】:

            无论在哪里使用函数式编程,扩展方法都会大放异彩。

            考虑一下您的应用中已经拥有的几乎所有模块级函数,以及当您将它们标记为扩展方法时它们会变成什么。它们成为“主动语态”而非“被动语态”的机会。主动语态意味着代码读起来好像实例提供了自己的方法来执行特定任务,而不是让函数被动地对其执行操作。

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

            对比

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

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

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

            1. 它们使您的函数(由于 Intellisense)更容易被发现。如果您提供了描述函数用途和用途的内联标记,Intellisense 甚至会向发现它的开发人员提供描述该方法及其用途的有用工具提示(只需按下点)。未标记为扩展方法的函数不容易被发现,可能会被闲置,因此,其他人可能会发明他们自己对所述函数的风格。

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

            【讨论】:

              【解决方案7】:

              我不知道任何性能影响。当您无法访问源代码并因此无法直接将方法添加到类中时,使用扩展方法最有意义,并且方法有意义可以作为函数实现。这转到我对您之前的问题所做的评论,其中另一个人提供了“字符串”类上的扩展方法的示例,该方法根据字符串是否为有效电子邮件返回布尔值。 IMO,这是不使用扩展方法的示例,因为该函数不是字符串类型的基础。但是,向“字符串”添加 Left(int) 和 Right(int) 函数确实有意义。

              【讨论】:

                【解决方案8】:

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

                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 定义文件并生成这些存根类,这些存根类代表我在数据库中拥有的一些对象。在这种情况下,唯一的方法是扩展它们。

                【讨论】:

                  【解决方案9】:

                  解决您的第二个问题:我的经验法则是扩展方法应该是类型上的“功能的自然扩展”,或者它应该是流畅接口的可维护和可读部分。

                  “功能的自然扩展”的一个示例是扩展数据读取器以在遇到 DBNull 时返回默认值。扩展数据读取器以返回由多个字段中的数据表示的实体的实例并不是那么自然。在后一种情况下,您将错误的职责注入到可怜的对象中:)。

                  【讨论】:

                    【解决方案10】:
                    /// <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;
                            }
                        }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-03-12
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-11-14
                      相关资源
                      最近更新 更多