ホームページ  >  記事  >  バックエンド開発  >  C# パラメータのこれは何を意味しますか (拡張メソッド)

C# パラメータのこれは何を意味しますか (拡張メソッド)

黄舟
黄舟オリジナル
2017-02-18 10:24:444346ブラウズ


拡張メソッドを使用すると、新しい派生型の作成、再コンパイル、または元の型の変更を行わなくても、既存の型にメソッドを「追加」できます。 拡張メソッドは特別な種類の静的メソッドですが、拡張タイプのインスタンス メソッドのように呼び出すことができます。 C# と Visual Basic で記述されたクライアント コードの場合、拡張メソッドを呼び出すことと、型で実際に定義されているメソッドを呼び出すことの間に大きな違いはありません。

最も一般的な拡張メソッドは、既存の System.Collections.IEnumerable 型と System.Collections.Generic.IEnumerable 型にクエリ機能を追加する LINQ 標準クエリ演算子です。 標準のクエリ演算子を使用するには、まず using を使用します System.Linq ディレクティブはそれらをスコープ内に置きます。 その後、IEnumerable を実装する型には、GroupBy、OrderBy、Average などのインスタンス メソッドがあるように見えます。 IEnumerable 型 (List や Array など) のインスタンスの後に「ドット」を入力すると、次のことができます。 これらの追加メソッドは、IntelliSense ステートメント補完に表示されます。

次の例は、整数の配列に対して標準クエリ演算子の OrderBy メソッドを呼び出す方法を示しています。 括弧内の式はラムダ式です。 多くの標準クエリ演算子はラムダ式をパラメータとして受け取りますが、これは拡張メソッドの要件ではありません。 詳細については Lambda を参照してください 式 (C# プログラミング ガイド)。

C#



class ExtensionMethods2    
{    static void Main()
    {            
        int[] ints = { 10, 45, 15, 39, 21, 26 };        var result = ints.OrderBy(g => g);        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }           
    }        
}//Output: 10 15 21 26 39 45


拡張メソッドは静的メソッドとして定義されていますが、インスタンス メソッド構文を通じて呼び出されます。 最初のパラメータは、メソッドが操作する型を指定し、パラメータの前に this 修飾子が付けられます。 拡張メソッドは、using ディレクティブを使用してソース コードに名前空間を明示的にインポートする場合にのみスコープ内に含まれます。

次の例は、System.String クラスに定義された拡張メソッドを示しています。 これは、ネストされていない非ジェネリックの静的クラス内で定義されていることに注意してください:

C#


namespace ExtensionMethods
{    public static class MyExtensions
    {        public static int WordCount(this String str)
        {            return str.Split(new char[] { ' ', '.', '?' }, 
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}


この using ディレクティブを使用して、WordCount 拡張メソッドを設定できます。範囲内:

using ExtensionMethods;


而且,可以使用以下语法从应用程序中调用该扩展方法:

string s = "Hello Extension Methods";
int i = s.WordCount();


在代码中,可以使用实例方法语法调用该扩展方法。 但是,编译器生成的中间语言 (IL) 会将代码转换为对静态方法的调用。 因此,并未真正违反封装原则。 实际上,扩展方法无法访问它们所扩展的类型中的私有变量。

有关详细信息,请参阅如何:实现和调用自定义扩展方法(C# 编程指南)。

通常,你更多时候是调用扩展方法而不是实现你自己的扩展方法。 由于扩展方法是使用实例方法语法调用的,因此不需要任何特殊知识即可从客户端代码中使用它们。 若要为特定类型启用扩展方法,只需为在其中定义这些方法的命名空间添加 using 指令。 例如,若要使用标准查询运算符,请将此 using 指令添加到代码中:


using System.Linq;


(你可能还必须添加对 System.Core.dll 的引用。)你将注意到,标准查询运算符现在作为可供大多数 IEnumerable 类型使用的附加方法显示在 IntelliSense 中。

C# パラメータのこれは何を意味しますか (拡張メソッド)C# パラメータのこれは何を意味しますか (拡張メソッド)

尽管标准查询运算符没有显示在 String 的 IntelliSense 中,但它们仍然可用。


コンパイル時に拡張メソッドをバインドする

拡張メソッドを使用してクラスまたはインターフェイスを拡張できますが、拡張メソッドをオーバーライドすることはできません。 インターフェイスまたはクラスメソッドと同じ名前とシグネチャを持つ拡張メソッドは決して呼び出されません。 コンパイル時、拡張メソッドは、型自体で定義されているインスタンス メソッドよりも常に優先度が低くなります。 言い換えると、型に Process(int i) メソッドで、同じシグネチャを持つ拡張メソッドがある場合、コンパイラーは常にそのインスタンス メソッドにバインドします。 コンパイラはメソッド呼び出しに遭遇すると、まずその型のインスタンス メソッドの中から一致するメソッドを探します。 一致するメソッドが見つからない場合、コンパイラはその型に定義されている拡張メソッドを検索し、最初に見つかった拡張メソッドにバインドします。 次の例は、コンパイラがどの拡張メソッドまたはインスタンス メソッドをバインドするかを決定する方法を示しています。

次の例は、メソッド呼び出しをインスタンス メソッドにバインドするか型の拡張メソッドにバインドするかを決定するときに C# コンパイラーが従う規則を示しています。 静的クラス Extensions には、IMyInterface を実装する任意の型に対して定義された拡張メソッドが含まれています。 クラス ABC はすべてこのインターフェースを実装しています。

MethodB 拡張メソッドは、その名前と署名がこれらのクラスによって既に実装されているメソッドと正確に一致するため、呼び出されることはありません。

コンパイラが一致するシグネチャを持つインスタンス メソッドを見つけられない場合、一致する拡張メソッド (そのようなメソッドが存在する場合) にバインドします。

C#


// Define an interface named IMyInterface.namespace DefineIMyInterface
{    using System;    public interface IMyInterface
    {        // Any class that implements IMyInterface must define a method
        // that matches the following signature.
        void MethodB();
    }
}// Define extension methods for IMyInterface.namespace Extensions
{    using System;    using DefineIMyInterface;    // The following extension methods can be accessed by instances of any 
    // class that implements IMyInterface.
    public static class Extension
    {        public static void MethodA(this IMyInterface myInterface, int i)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, int i)");
        }        public static void MethodA(this IMyInterface myInterface, string s)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, string s)");
        }        // This method is never called in ExtensionMethodsDemo1, because each 
        // of the three classes A, B, and C implements a method named MethodB
        // that has a matching signature.
        public static void MethodB(this IMyInterface myInterface)
        {
            Console.WriteLine
                ("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}// Define three classes that implement IMyInterface, and then use them to test// the extension methods.namespace ExtensionMethodsDemo1
{    using System;    using Extensions;    using DefineIMyInterface;    class A : IMyInterface
    {        public void MethodB() { Console.WriteLine("A.MethodB()"); }
    }    class B : IMyInterface
    {        public void MethodB() { Console.WriteLine("B.MethodB()"); }        
    public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
    }    class C : IMyInterface
    {        public void MethodB() { Console.WriteLine("C.MethodB()"); }        
    public void MethodA(object obj)
        {
            Console.WriteLine("C.MethodA(object obj)");
        }
    }    class ExtMethodDemo
    {        static void Main(string[] args)
        {            // Declare an instance of class A, class B, and class C.
            A a = new A();
            B b = new B();
            C c = new C();            // For a, b, and c, call the following methods:
            //      -- MethodA with an int argument
            //      -- MethodA with a string argument
            //      -- MethodB with no argument.

            // A contains no MethodA, so each call to MethodA resolves to 
            // the extension method that has a matching signature.
            a.MethodA(1);           // Extension.MethodA(object, int)
            a.MethodA("hello");     // Extension.MethodA(object, string)

            // A has a method that matches the signature of the following call
            // to MethodB.
            a.MethodB();            // A.MethodB()

            // B has methods that match the signatures of the following
            // method calls.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()

            // B has no matching method for the following call, but 
            // class Extension does.
            b.MethodA("hello");     // Extension.MethodA(object, string)

            // C contains an instance method that matches each of the following
            // method calls.
            c.MethodA(1);           // C.MethodA(object)
            c.MethodA("hello");     // C.MethodA(object)
            c.MethodB();            // C.MethodB()
        }
    }
}/* Output:
    Extension.MethodA(this IMyInterface myInterface, int i)
    Extension.MethodA(this IMyInterface myInterface, string s)
    A.MethodB()
    B.MethodA(int i)
    B.MethodB()
    Extension.MethodA(this IMyInterface myInterface, string s)
    C.MethodA(object obj)
    C.MethodA(object obj)
    C.MethodB()
 */

通用准则

通常,建议你只在不得已的情况下才实现扩展方法,并谨慎地实现。 只要有可能,必须扩展现有类型的客户端代码都应该通过创建从现有类型派生的新类型来达到这一目的。 有关详细信息,请参阅继承(C# 编程指南)。

在使用扩展方法来扩展你无法更改其源代码的类型时,你需要承受该类型实现中的更改会导致扩展方法失效的风险。

如果你确实为给定类型实现了扩展方法,请记住以下几点:

  • 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。

  • 在命名空间级别将扩展方法置于范围中。 例如,如果你在一个名为 Extensions 的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由 using Extensions; 指令置于范围中。

针对已实现的类库,不应为了避免程序集的版本号递增而使用扩展方法。 如果要向你拥有源代码的库中添加重要功能,应遵循适用于程序集版本控制的标准 .NET Framework 准则。 有关详细信息,请参阅程序集版本控制。

 以上就是C# 参数带this是什么意思(扩展方法)的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。