什么是扩展方法?

56

.NET 中的扩展方法是什么?



编辑: 我在使用扩展方法上发布了一个后续问题。


1
请查看下面我的回答。基本上,您应该创建扩展而不是使用继承。 - Tom Anderson
@Tom Anderson - 那么创建扩展方法的决定主要是基于对源代码的访问吗? - Developer
1
在我的情况下,是的。另一个例子是便利性,而不是继承自System.Windows.Forms.Form并添加方法,然后修改所有源代码以使用该基类,我只需编写扩展程序,现在所有窗体都具有它。 - Tom Anderson
1
试着看一下这个问题。 https://dev59.com/0HRC5IYBdhLWcg3wOeSB - Tom Anderson
以下是关于C#扩展方法的简洁文章,包含足够的信息以帮助你入门:在C#和.NET中如何使用扩展方法及其使用时机 - Roboblob
https://www.codingcrest.com/extension-method-in-c/ - Imad
13个回答

69
扩展方法允许开发者向现有CLR类型的公共契约中添加新的方法,而无需对其进行子类化或重新编译原始类型。扩展方法有助于结合今天在动态语言中流行的“鸭子类型”支持的灵活性和强类型语言的性能和编译时验证。

参考: http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx

以下是一个扩展方法的示例(请注意第一个参数前面的“this”关键字):

public static bool IsValidEmailAddress(this string s)
{
    Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
    return regex.IsMatch(s);
}

现在,可以直接从任何字符串中调用上述方法,如下所示:
bool isValid = "so@mmas.com".IsValidEmailAddress();

添加的方法将会出现在 IntelliSense 中:

alt text
(来源:scottgu.com)

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

看一下以下示例:

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/


5
虽然我对将扩展方法添加到诸如字符串之类的基本类型而不是该类型的基本部分有所不满。因此,我不会将“IsValidEmailAddress”添加到字符串中,但我已经添加了Left(int)和Right(int)。 - Michael Bray

14

扩展方法 - 简单解释

即使您无法访问原始源代码,也可以向某些内容添加方法。 让我们考虑一个例子来解释这一点:

例子:

假设我有一只狗。 所有狗 - 所有类型为狗的动物 - 都会做某些事情:

  1. 摇尾巴
  2. 哭“汪 / 旺!”等

狗能做的所有事情都被称为“方法”。 现在假设OO天堂中的伟大程序员忘记向狗类添加一个方法:FetchNewspaper()。 您希望能够说:

rex.FetchNewspaper(); // or
beethoven.FetchNewspaper(); 

尽管您无法访问源代码,但仍然可以这样做。

你是如何让你的狗这样做的? 您唯一的解决方案是创建一个“扩展方法”。

创建扩展方法

(请注意下面第一个参数前面的“this”关键字):

public static void FetchNewsPaper(this Dog familyDog)
{
     Console.Writeline(“Goes to get newspaper!”)
}

如果你想让你的狗拿报纸,只需这样做:

Dog freddie_the_family_dog = new Dog();

freddie_the_family_dog.FetchNewspaper();

您可以向一个类添加方法而无需拥有源代码。这非常方便!


5
扩展方法是开发人员为了“添加”方法到他们无法控制的对象而采用的方法。
例如,如果您想要将“DoSomething()”方法添加到System.Windows.Forms对象中,但由于您无法访问该代码,因此您只需为表单创建一个扩展方法,其语法如下。
Public Module MyExtensions

    <System.Runtime.CompilerServices.Extension()> _
    Public Sub DoSomething(ByVal source As System.Windows.Forms.Form)
        'Do Something
    End Sub

End Module

现在你可以在表单中调用“Me.DoSomething()”来实现某些功能。总的来说,这是一种在不使用继承的情况下为现有对象添加功能的方法。

5

扩展方法是一种“编译器技巧”,它允许您模拟向另一个类添加方法的操作,即使您没有该类的源代码。

例如:

using System.Collections;
public static class TypeExtensions
{
    /// <summary>
    /// Gets a value that indicates whether or not the collection is empty.
    /// </summary>
    public static bool IsEmpty(this CollectionBase item)
    {
        return item.Count == 0;
    } 
}

理论上,现在所有的集合类都包括一个IsEmpty方法,如果该集合没有任何项,则返回true(前提是您已经在上面包含定义该类的命名空间)。

如果我漏掉了重要的东西,我相信有人会指出来(请告诉我!)。

当然,对于扩展方法的声明有规定(它们必须是静态的,第一个参数必须以this关键字开头等等)。

扩展方法实际上不会修改它们看起来要扩展的类;相反,编译器会混淆函数调用,以便在运行时正确地调用该方法。但是,扩展方法将适当地显示在 IntelliSense 下拉菜单中,并且您可以像普通方法一样记录它们(如上所示)。

注意:如果已经存在一个具有相同签名的方法,则扩展方法永远不会替换该方法。


最好的解释。谢谢Mike。 - Gurusinghe

1

1
这是VB.Net的示例;请注意Extension()属性。将其放置在项目中的模块中。
Imports System.Runtime.CompilerServices
<Extension()> _
Public Function IsValidEmailAddress(ByVal s As String) As Boolean
    If String.IsNullOrEmpty(s) Then Return False

    Return Regex.IsMatch(email, _
         "^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")
End Function

1

扩展方法是一种技术,通过它我们可以为现有的数据类型添加方法。以下是如何向现有数据类型添加方法的示例。

  1. 我们创建了一个扩展方法类,在其中创建了各种扩展方法

代码:

public static class ExtensionMethods
{
    public static int WordCount(this string str)
    {
        return str.Split(' ').Count();
    }

    public static int LongestWord(this string str)
    {
        return str.Split(' ').Select(x =>
                x.Length).OrderByDescending(y =>
                y).FirstOrDefault();
    }
}
  1. 以下是程序类,我们在其中调用了上面类中指定的扩展方法。

代码:

public static void Main(String[] args)
{
    string str = "this is my name    punit";
    int i = str.LongestWord();
}

要点:

  • 扩展方法类需要是静态的

  • 扩展方法的第一个参数将具有this关键字,该方法也应该是静态的


0

C#中的扩展方法

什么是扩展方法?

Extension method means Class name and method name are having with Static Key word.

扩展方法使用“this”关键字,第一个签名应该是标识地址,这种类型必须与相同的类型相同,

public static string Meth(this string i,int j)

string res = "ae".Meth(num);

粗体部分是标识地址。命名空间应该相同。

为什么我们需要扩展?

我们在许多地方使用相同的函数,之后无法找到为什么在这里使用。因此使用地址标识。静态意味着单个实例。这只能访问静态成员函数。即使如何在静态类中访问非静态类方法?例如:查看“Sal”方法中抛出错误,

static class Emp     {         public int Sal(int i,int j)         {             return i * j;         }     }

仅访问静态成员。和

enter image description here

在该代码之后,我们无法在静态类中为非静态类创建对象。那么如何在静态类中访问非静态方法。因此,我们遵循扩展方法而不是继承。

例如,在此处使用扩展方法添加或甚至功能,

使用 System.Text;

使用 System.Threading.Tasks;

命名空间 Extension_Method { 静态类 Program {

    public static string Meth (this string i,int j)
    {
        if (j % 2 == 0)
        {
            return "Even";
        }
        else
        {
            return "ODD";

        }
    }


}
class program2
{
    static void Main (string [] args)
    {
        Console. WriteLine ("Enter Integer");
        int num = Convert.ToInt32(Console.ReadLine());

        string res = "ae".Meth(num);

        Console. WriteLine(res);
        Console.ReadLine();
    }
}

结果:

在此输入图片描述


0

我想就扩展方法回答一个密封类的问题。

假设您有一个包含重要方法的密封类。现在您想要添加一个新方法。假设这个类来自外部DLL,因此您无法向其中添加新方法。那么你会怎么做?

再举一个例子。假设该类是由您的项目经理或安全专家在应用程序中构建的。您只能通过获得项目经理或安全专家的许可来编辑该类。

您的解决方案可能是从该类中删除Sealed关键字并将其继承到其他类中。然后,在子类(继承此类的类)中,您可以添加新方法。

但是等等->您不能简单地删除Sealed关键字,因为这可能会危及整个应用程序的安全性。我敢打赌,您的项目经理或安全专家永远不会给您那个权限,引用安全问题。

那么在这里可以做什么?

答案是扩展方法,它们可以添加到静态类中,您甚至不需要再触摸密封类。

例如:

sealed class MyData
{
    private double D1, D2, D3;
    public MyData(double d1, double d2, double d3)
    { D1 = d1; D2 = d2; D3 = d3; }
    public double Sum() { return D1 + D2 + D3; }
}

static class ExtendMyData
{
    public static double Average(this MyData md)
    {
        return md.Sum() / 3;
    }
}

我在一个新的静态类中添加了一个名为Average的公共静态方法。该方法具有使用“this”关键字前缀的主类参数。

现在,要调用Average方法,我使用父封闭类的对象而不是子类的对象。

 MyData md = new MyData(3, 4, 5);
 double exAverage = md.Average();

希望你觉得扩展方法有用。

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;
        }
    }
}

代码示例总是一个好答案。但是当有支持性的评论说明该代码如何改进/修复问题时,答案的质量会大大提高。 - benhorgen

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