Cách tạo các hàm nội tuyến trong C #


98

Tôi đang sử dụng Linq To XML

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

nhưng tôi muốn thực hiện một số tính toán đối với tiền tố trước khi thêm nó vào xml, như loại bỏ dấu cách, ký tự đặc biệt, một số phép tính, v.v.

Tôi không muốn tạo các hàm vì những hàm này sẽ không giúp ích gì cho bất kỳ phần nào khác của chương trình của tôi nhưng điều này, vậy có cách nào để tạo các hàm nội tuyến không ??


5
Đừng cấm bản thân tạo một hàm chỉ vì nó sẽ không được gọi từ bất kỳ nơi nào khác. Các hàm cũng có thể được sử dụng để làm sạch mã (nếu bạn có một thủ tục lớn kéo dài nhiều trang, chia nó thành các hàm giúp nó dễ đọc hơn) và tự tài liệu (chỉ cần đặt tên một hàm đúng cách có thể truyền tải thông tin đến người đọc.)
Joe

14
Nếu bạn đến đây từ Google và muốn biết cách mô phỏng các hàm nội tuyến trong c #, hãy truy cập vào đây: stackoverflow.com/questions/473782/inline-functions-in-c Các câu trả lời bên dưới không liên quan đến các hàm "nội tuyến" theo đúng nghĩa của từ này, và c # thậm chí không có các hàm nội tuyến thực sự nhưng liên kết ở trên cung cấp một tối ưu hóa trình biên dịch có thể được sử dụng.
JamesHoux

Cảm ơn James, tiêu đề câu hỏi thực sự là khá sai lầm.
Zar Shardan

Câu trả lời:


228

Có, C # hỗ trợ điều đó. Có một số cú pháp có sẵn.

  • Các phương thức ẩn danh đã được thêm vào 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!");
    }
    
  • Lambdas mới trong C # 3.0 và có hai hương vị.

    • Biểu thức lambdas:

      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
      
    • Tuyên bố lambdas:

      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ác hàm cục bộ đã được giới thiệu với C # 7.0:

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

Về cơ bản có hai loại khác nhau cho những điều này: FuncAction. Funcs trả về giá trị nhưng Actionkhông. Tham số kiểu cuối cùng của a Funclà kiểu trả về; tất cả những cái khác là các loại tham số.

Có những loại tương tự với tên khác nhau, nhưng cú pháp để khai báo chúng nội dòng là như nhau. Một ví dụ về điều này là Comparison<T>, gần tương đương với 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

Chúng có thể được gọi trực tiếp như thể chúng là các phương thức thông thường:

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
Tôi không hiểu câu trả lời này. Trong ví dụ của mình, anh ta muốn XElement mới ("Tiền tố", tiền tố => newPrefix) được khai báo nội tuyến. Tất cả những câu trả lời này có nghĩa là khai báo một hàm mới. Tại sao không chỉ khai báo một hàm mới?
Matthew James Davis

3
bởi vì: 1. logic undertand của phương pháp đọc chức năng inline là dễ dàng hơn nhiều so với tìm kiếm tờ khai khác nhau 2. thường xuyên bạn cần phải viết mã ít hơn khi sử dụng chức năng inline
Volodymyr

35

C # 7 bổ sung hỗ trợ cho các chức năng cục bộ

Đây là ví dụ trước sử dụng hàm cục bộ

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

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

30

Câu trả lời cho câu hỏi của bạn là có và không, tùy thuộc vào ý bạn muốn nói đến "hàm nội tuyến". Nếu bạn đang sử dụng thuật ngữ giống như nó được sử dụng trong phát triển C ++ thì câu trả lời là không, bạn không thể làm điều đó - ngay cả một biểu thức lambda cũng là một lời gọi hàm. Mặc dù đúng là bạn có thể xác định các biểu thức lambda nội tuyến để thay thế các khai báo hàm trong C #, nhưng cuối cùng trình biên dịch vẫn tạo ra một hàm ẩn danh.

Đây là một số mã thực sự đơn giản mà tôi đã sử dụng để kiểm tra điều này (VS2015):

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

Trình biên dịch tạo ra gì? Tôi đã sử dụng một công cụ tiện lợi có tên ILSpy hiển thị lắp ráp IL thực tế được tạo. Hãy xem (Tôi đã bỏ qua rất nhiều công cụ thiết lập lớp)

Đây là chức năng chính:

        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

Xem những dòng IL_0026 và IL_0027? Hai lệnh đó tải số 5 và gọi một hàm. Sau đó định dạng IL_0031 và IL_0036 và in kết quả.

Và đây là hàm được gọi là:

        .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'

Đó là một hàm thực sự ngắn, nhưng nó là một hàm.

Điều này có đáng để nỗ lực tối ưu hóa không? Không. Có thể nếu bạn đang gọi nó hàng nghìn lần một giây, nhưng nếu hiệu suất là quan trọng thì bạn nên cân nhắc gọi mã gốc được viết bằng C / C ++ để thực hiện công việc.

Theo kinh nghiệm của tôi, khả năng đọc và khả năng bảo trì hầu như luôn quan trọng hơn việc tối ưu hóa để đạt được tốc độ vài micro giây. Sử dụng các hàm để làm cho mã của bạn có thể đọc được và kiểm soát phạm vi thay đổi và không lo lắng về hiệu suất.

"Tối ưu hóa sớm là gốc rễ của mọi điều xấu (hoặc ít nhất là hầu hết) trong lập trình." - Donald Knuth

"Một chương trình không chạy chính xác thì không cần chạy nhanh" - Tôi


4
Đây là một thành công hàng đầu của Google cho các hàm C # nội tuyến. Bạn nói "Có thể nếu bạn đang gọi nó hàng nghìn lần một giây" và đoán xem - tôi đang gọi nó 4 triệu lần một giây, vì vậy, vâng ... nội tuyến có thể sẽ hữu ích (và tăng đáng kể khả năng đọc mã so với những gì Tôi sắp phải làm: giải nén thủ công một chức năng ở 16 vị trí).
Adam

Đối với những người quen thuộc với Kotlin, điều này có nghĩa là các inlinehàm của Kotlin không có hàm tương đương trong C #.
Vapid Linus

17

Đúng.

Bạn có thể tạo các phương thức ẩn danh hoặc biểu thức lambda :

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

Cảm ơn câu trả lời của bạn, bạn có thể giải thích thêm một chút về câu trả lời, câu trả lời lamba, ý ​​tôi là codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codeim mới này và tôi thực sự không hiểu rất rõ doc MSDN
Joe Dixon

2
@Joe: x ?? ""là toán tử liên kết rỗng. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks

In x => (something), xlà một tham số và (something)là giá trị trả về`.
SLaks


2
@Hellfrost: Đọc câu hỏi. Anh ấy không hỏi về các hàm nội tuyến kiểu C ++. Tôi không muốn tạo các hàm vì những hàm này sẽ không giúp ích được gì cho bất kỳ phần nào khác trong chương trình của tôi nhưng điều này
SLaks

5

Bạn có thể sử dụng Func để đóng gói một phương thức có một tham số và trả về một giá trị của kiểu được chỉ định bởi tham số TResult.

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


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

4

Không chỉ các phương thức Inside, nó cũng có thể được sử dụng bên trong các lớp.

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);
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.