C#でインライン関数を作成する方法


98

Linq ToXMLを使用しています

new XElement("Prefix", Prefix == null ? "" : Prefix)

しかし、スペース、特殊文字、いくつかの計算などを削除するなど、接頭辞をxmlに追加する前にいくつかの計算を行いたい

これは私のプログラムの他の部分では役に立たないので、関数を作成したくありませんが、これは、インライン関数を作成する方法はありますか?


5
他の場所から呼び出されないという理由だけで関数を作成することを禁止しないでください。関数を使用して、コードをクリーンアップすることもでき(複数のページにまたがる大きなプロシージャを関数に分割して読みやすくする場合)、セルフドキュメント(関数に適切な名前を付けるだけで読者に情報を伝えることができます)
Joe

14
Googleからここに来て、c#でインライン関数をエミュレートする方法を知りたい場合は、ここにアクセスしてください:stackoverflow.com/questions/473782/inline-functions-in-c 以下の回答は「インライン」関数には関係しません。本当の意味で、c#には真のインライン関数すらありませんが、上記のリンクは、使用できるコンパイラーの最適化を提供します。
JamesHoux 2018年

ジェームズに感謝します、質問のタイトルは確かにかなり誤解を招くものです。
ZarShardan18年

回答:


227

はい、C#はそれをサポートしています。利用可能な構文はいくつかあります。

  • 匿名メソッドがC#2.0で追加されました。

    Func<int, int, int> add = delegate(int x, int y)
    {
        return x + y;
    };
    Action<int> print = delegate(int x)
    {
        Console.WriteLine(x);
    }
    Action<int> helloWorld = delegate // parameters can be elided if ignored
    {
        Console.WriteLine("Hello world!");
    }
    
  • ラムダはC#3.0の新機能であり、2つのフレーバーがあります。

    • 式ラムダ:

      Func<int, int, int> add = (int x, int y) => x + y; // or...
      Func<int, int, int> add = (x, y) => x + y; // types are inferred by the compiler
      
    • ステートメントラムダ:

      Action<int> print = (int x) => { Console.WriteLine(x); };
      Action<int> print = x => { Console.WriteLine(x); }; // inferred types
      Func<int, int, int> add = (x, y) => { return x + y; };
      
  • ローカル関数はC#7.0で導入されました。

    int add(int x, int y) => x + y;
    void print(int x) { Console.WriteLine(x); }
    

これらには基本的に2つの異なるタイプがあります:FuncActionFuncsは値を返しますが、Actionsは返しません。aの最後の型パラメーターはFunc戻り値の型です。他のすべてはパラメータタイプです。

異なる名前の類似したタイプがありますが、それらをインラインで宣言するための構文は同じです。この例はComparison<T>、です。これは、とほぼ同等Func<T, T, int>です。

Func<string, string, int> compare1 = (l,r) => 1;
Comparison<string> compare2 = (l, r) => 1;
Comparison<string> compare3 = compare1; // this one only works from C# 4.0 onwards

これらは、通常のメソッドであるかのように直接呼び出すことができます。

int x = add(23, 17); // x == 40
print(x); // outputs 40
helloWorld(x); // helloWorld has one int parameter declared: Action<int>
               // even though it does not make any use of it.

4
私はこの答えを理解していません。彼の例では、新しいXElement( "Prefix"、prefix => newPrefix)をインラインで宣言する必要があります。これらの答えはすべて、新しい関数を宣言することになります。新しい関数を宣言してみませんか?
マシュージェームスデイビス

3
理由:1。インライン関数を読み取るメソッドのロジックを理解することは、さまざまな宣言を検索するよりもはるかに簡単です。2。インライン関数を使用するときに必要なコードが少なくなることがよくあります
Volodymyr 2016年

35

C#7はローカル関数のサポートを追加します

これは、ローカル関数を使用した前の例です

void Method()
{
    string localFunction(string source)
    {
        // add your functionality here
        return source ;
    };

   // call the inline function
   localFunction("prefix");
}

29

「インライン関数」の意味に応じて、質問に対する答えは「はい」と「いいえ」です。C ++開発で使用されているような用語を使用している場合、答えはノーです。それはできません。ラムダ式でさえ関数呼び出しです。インラインラムダ式を定義してC#の関数宣言を置き換えることができるのは事実ですが、コンパイラーは無名関数を作成することになります。

これをテストするために使用した非常に単純なコードを次に示します(VS2015)。

    static void Main(string[] args)
    {
        Func<int, int> incr = a => a + 1;
        Console.WriteLine($"P1 = {incr(5)}");
    }

コンパイラは何を生成しますか?生成された実際のILアセンブリを表示するILSpyと呼ばれる気の利いたツールを使用しました。見てください(私は多くのクラス設定のものを省略しました)

これがメイン機能です。

        IL_001f: stloc.0
        IL_0020: ldstr "P1 = {0}"
        IL_0025: ldloc.0
        IL_0026: ldc.i4.5
        IL_0027: callvirt instance !1 class [mscorlib]System.Func`2<int32, int32>::Invoke(!0)
        IL_002c: box [mscorlib]System.Int32
        IL_0031: call string [mscorlib]System.String::Format(string, object)
        IL_0036: call void [mscorlib]System.Console::WriteLine(string)
        IL_003b: ret

それらの行IL_0026およびIL_0027を参照してください。これらの2つの命令は、数値5をロードし、関数を呼び出します。次に、IL_0031およびIL_0036をフォーマットして、結果を出力します。

そして、これが呼ばれる関数です:

        .method assembly hidebysig 
            instance int32 '<Main>b__0_0' (
                int32 a
            ) cil managed 
        {
            // Method begins at RVA 0x20ac
            // Code size 4 (0x4)
            .maxstack 8

            IL_0000: ldarg.1
            IL_0001: ldc.i4.1
            IL_0002: add
            IL_0003: ret
        } // end of method '<>c'::'<Main>b__0_0'

本当に短い関数ですが、関数です。

これは最適化するために努力する価値がありますか?いや。1秒間に何千回も呼び出す場合でも、パフォーマンスがそれほど重要な場合は、C / C ++で記述されたネイティブコードを呼び出して作業を行うことを検討する必要があります。

私の経験では、読みやすさと保守性は、ほとんどの場合、数マイクロ秒の速度向上を最適化するよりも重要です。関数を使用して、コードを読みやすくし、変数のスコープを制御し、パフォーマンスについて心配する必要はありません。

「時期尚早の最適化は、プログラミングにおけるすべての悪(または少なくともそのほとんど)の根源です。」-ドナルド・クヌース

「正しく実行されないプログラムは速く実行する必要はありません」-私


3
これは、C#関数をインライン化するためのGoogleのトップヒットです。あなたは「多分あなたがそれを毎秒数千回呼んでいるなら」と言って、何を推測します-私はそれを毎秒400万回呼んでいるので、ええ...インライン化はおそらく役に立つでしょう(そしてコードの可読性を何よりも大幅に向上させます私がやらなければならないことです:16か所で1つの関数を手動で展開します)。
アダム

17

はい。

匿名メソッドまたはラムダ式を作成できます:

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";

回答ありがとうございます。ランバの回答についてもう少し詳しく説明してcodeいただけますか。つまり、Func <string、string> PrefixTrimmer = x => x ?? ""; codeこれは初めてで、MSDNドキュメントをよく理解していません
Joe Dixon

2
@Joe:x ?? ""はnull合体演算子です。 msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks 2011

x => (something)xはパラメータであり、(something)は戻り値です。
SLaks 2011


2
@Hellfrost:質問を読んでください。彼はC ++スタイルのインライン関数について質問していません。関数を作成したくないのは、これは私のプログラムの他の部分では役に立たないからですが、これは
SLaks 2011

5

1つのパラメーターを持つメソッドをカプセル化し、TResultパラメーターで指定されたタイプの値を返すFuncを使用できます。

void Method()
{
    Func<string,string> inlineFunction = source => 
    {
        // add your functionality here
        return source ;
     };


    // call the inline function
   inlineFunction("prefix");
}

4

Insideメソッドだけでなく、クラス内でも使用できます。

class Calculator
    {
        public static int Sum(int x,int y) => x + y;
        public static Func<int, int, int>  Add = (x, y) => x + y;
        public static Action<int,int> DisplaySum = (x, y) => Console.WriteLine(x + y);
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.