Tại sao các thuộc tính không hoàn toàn có thể chuyển đổi cho các đại biểu


9

Chúng ta đều biết rằng các thuộc tính trong C # được biên dịch theo các phương thức cũ đơn giản thực tế. Nhưng không giống như các phương thức (-group), chúng không thể được đưa ra làm đối số cho các phương thức khác mong đợi một đại biểu như một Func<T>hoặc Action<T>(getter và setter). Có điều gì đặc biệt cấm điều này không, hay nó chỉ là một tính năng không "miễn phí" khi tạo các nhóm phương thức hoàn toàn có thể chuyển đổi cho các đại biểu?

Mã ví dụ:

public class MyClass
{
    public static int X { get; set; }
}

private static void Foo<T>(Func<T> f)
{
    Console.WriteLine(f());
}

private static void Bar<T>(Action<T> f)
{
    f(default(T));
}

private static void Test()
{
    // neither of these lines compile, even with an explicit type-cast.
    Foo(MyClass.X);
    Bar(MyClass.X);
}

Nếu tôi phải đoán, tôi sẽ nói rằng vấn đề cú pháp phân biệt giữa các yêu cầu và tham chiếu đến chính tài sản đó không đáng để giải quyết khi chúng ta có thể viết thêm () => MyClass.Xhoặc viết x => MyClass.X = x.


Mà họ sẽ gọi? Nhận hoặc đặt
Ewan

Tôi nghĩ rằng nó sẽ phụ thuộc vào loại đại biểu. setlà một Actiongetlà một Funcví dụ
sara

1
FYI, cú pháp sử dụng các đại biểu nội tuyến để vượt qua getter / setter . delegate {return SomeProperty}vượt qua getter; delegate(YourType value) {SomeProperty = value}vượt qua setter. Nếu bạn chỉ cần vượt qua một hoặc hai địa điểm, đây là cách viết tắt cho các phương thức được bao bọc mà bạn sẽ tạo.
ToolmakerSteve

Và FWIW, nếu bạn thấy mình muốn vượt qua cả getter và setter, thay vào đó hãy xem xét việc xác định một thuộc tính Interfaceđó và thay đổi chữ ký của phương thức được gọi để lấy giao diện đó làm tham số.
ToolmakerSteve

Câu trả lời:


7

Vấn đề là "MyClass.X" có nghĩa được xác định rõ, đó là gọi hàm getter trên thuộc tính "X". Điều quan trọng cần lưu ý là, mặc dù một phương thức đang được gọi, dấu ngoặc không được yêu cầu.

Mặt khác, khi gọi một phương thức thông thường, chẳng hạn như khi bạn gọi "Foo" trong mã ví dụ của mình, các dấu ngoặc được yêu cầu để chỉ ra rằng phương thức đó sẽ được thực thi. Nếu bạn muốn chuyển tham chiếu đến một phương thức thì bạn sẽ loại trừ dấu ngoặc (xem ví dụ bên dưới).

Điều này có nghĩa là không có sự mơ hồ khi tham chiếu một phương thức - hoặc bạn đang thực thi nó ngay lập tức (chuyển bất kỳ đối số nào mà nó yêu cầu và bao gồm cả dấu ngoặc) hoặc bạn đang truyền phương thức dưới dạng đối số (không có dấu ngoặc).

Với một getter hoặc setter thuộc tính, không có dấu ngoặc nào có nghĩa là "thực hiện getter / setter ngay lập tức". Nếu thuộc tính getter / setter cũng có thể được chuyển thành một nhóm phương thức thì đôi khi "x.Name" có nghĩa là "thực thi Tên getter ngay bây giờ" và đôi khi nó có nghĩa là "truyền Tên getter như một nhóm phương thức". Mặc dù có thể thay đổi trình biên dịch thành định hướng dựa trên việc "x.Name" có vẻ như được chuyển vào một hàm mong đợi một chuỗi (trong trường hợp đó, getter sẽ được thực thi) hoặc liệu nó có bị chuyển vào không một hàm mong đợi một Func <chuỗi> (trong trường hợp đó, getter sẽ được chuyển thành một nhóm phương thức), nhóm ngôn ngữ C # cố gắng tránh các loại kịch bản có khả năng gây nhầm lẫn này.

using System;

namespace Example
{
    public static class Program
    {
        static void Main()
        {
            ValueWriter(1); // Execute ValueWriter (because brackets)
            Bar(ValueWriter); // Pass ValueWriter as an action (no brackets)
        }

        private static void Bar(Action<int> f)
        {
            f(2);
        }

        private static void ValueWriter(int value)
        {
            Console.WriteLine("Value is " + value);
        }
    }
}

5

Không thể do các nguyên tắc cơ bản về cách thức hoạt động của ngữ pháp và trình biên dịch.

Biểu thức được đánh giá "từ dưới lên", có nghĩa là biểu thức MyClass.Xước tính giá trị int của nó trước khi kết quả này được cung cấp dưới dạng đối số của hàm. Đề xuất của bạn dường như đề xuất rằng bối cảnh - loại tham số - có thể định hướng nếu biểu thức nên được hiểu là một lời gọi getter hoặc như một sự giới thiệu cho chính phương thức getter. Điều này là không thể làm một cách rõ ràng. Điều gì xảy ra nếu loại tài sản đó là một Funchoặc Action? Và sẽ var x = MyClass.Xđược giải thích như thế nào? Điều gì xảy ra nếu phương thức có quá tải với một inthoặc Funchoặc Actiontham số?

Để giải quyết những sự mơ hồ này, bạn cần có sự phân biệt ở cấp độ cú pháp, ví dụ: một toán tử có hỗ trợ đặc biệt về ngữ pháp như typeoftoán tử, ví dụ:

Foo(getterof(MyClass.X));

Nhưng điều này sẽ là rất nhiều rắc rối cho một tính năng với lợi ích hạn chế như vậy.


Vâng, một người tinh tế có thể làm việc với, nếu một người phải, trong khi tính không ổn định là một điểm dừng hiển thị.
Ded repeatator

2

Foo.Do()có nghĩa là sự mời gọi và Foo.Dotrở thành, đại biểu là tốt.

Foo.Xlà tài sản cũng như đại biểu getter và đại biểu setter tùy thuộc vào sự khác biệt bối cảnh tinh tế là khó hiểu. Các tính năng tốt không quá tốt thậm chí không có trong C #, đây có vẻ như là một tính năng thực sự xấu. Nếu bạn muốn viết ngắn một lần mã hãy thử Perl.

Đã tồn tại một cách để thể hiện rõ ràng cái nào bạn cần trong C #, tôi không thấy vấn đề.

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.