Tại sao C # cấm các loại thuộc tính chung?


510

Điều này gây ra ngoại lệ thời gian biên dịch:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Tôi nhận ra C # không hỗ trợ các thuộc tính chung. Tuy nhiên, sau nhiều lần Googling, tôi dường như không thể tìm thấy lý do.

Có ai biết tại sao các loại chung chung không thể xuất phát từ Attribute? Bất kỳ lý thuyết?


17
Bạn có thể thực hiện [Xác thực (typeof (chuỗi)] - Tôi đồng ý rằng thuốc generic sẽ đẹp hơn ...
Tham khảoUtah

20
Mặc dù đây là một bổ sung rất muộn cho câu hỏi này, thật đáng buồn khi không chỉ các thuộc tính mà cả các lớp thuộc tính trừu tượng (rõ ràng không thể được khởi tạo như các thuộc tính dù sao) không abstract class Base<T>: Attribute {}được sử dụng , như thế này: có thể được sử dụng để tạo ra không phải các lớp dẫn xuất chung như thế này:class Concrete: Base<MyType> {}
Lucero

88
Tôi khao khát các thuộc tính chung và các thuộc tính chấp nhận lambdas. Hãy tưởng tượng những thứ như [DependsOnProperty<Foo>(f => f.Bar)]hoặc [ForeignKey<Foo>(f => f.IdBar)]...
Jacek Gorgoń

3
Điều này sẽ cực kỳ hữu ích trong một tình huống tôi vừa gặp phải; sẽ thật tuyệt khi tạo một LinkedValueAttribution chấp nhận một loại chung và thực thi loại đó trên giá trị thực được chỉ định. Tôi có thể sử dụng nó cho enum để chỉ định giá trị "mặc định" của một enum khác nên được sử dụng nếu giá trị enum này được chọn. Nhiều thuộc tính này có thể được chỉ định cho các loại khác nhau và tôi có thể nhận được giá trị tôi cần dựa trên loại tôi cần. Tôi có thể thiết lập nó để sử dụng Type và Object nhưng được gõ mạnh sẽ là một điểm cộng rất lớn.
KeithS

10
Nếu bạn không phiền một chút IL, điều này có vẻ đầy hứa hẹn .
Jarrod Dixon

Câu trả lời:


358

Chà, tôi không thể trả lời tại sao nó không có sẵn, nhưng tôi có thể xác nhận rằng đó không phải là vấn đề CLI. Thông số CLI không đề cập đến nó (theo như tôi có thể thấy) và nếu bạn sử dụng IL trực tiếp, bạn có thể tạo một thuộc tính chung. Một phần của thông số C # 3 cấm nó - phần 10.1.4 "Đặc tả cơ sở lớp" không đưa ra bất kỳ lời biện minh nào.

Thông số ECMA C # 2 được chú thích cũng không cung cấp bất kỳ thông tin hữu ích nào, mặc dù nó cung cấp một ví dụ về những gì không được phép.

Bản sao của thông số C # 3 được chú thích sẽ xuất hiện vào ngày mai ... Tôi sẽ xem liệu điều đó có cung cấp thêm thông tin nào không. Dù sao, đó chắc chắn là một quyết định ngôn ngữ chứ không phải là thời gian chạy.

EDIT: Trả lời từ Eric Lippert (paraphrased): không có lý do cụ thể, ngoại trừ để tránh sự phức tạp trong cả ngôn ngữ và trình biên dịch cho một trường hợp sử dụng không thêm nhiều giá trị.


139
"ngoại trừ để tránh sự phức tạp trong cả ngôn ngữ và trình biên dịch" ... và điều đó từ những người cho chúng ta hợp tác và chống lại ...
flq

254
"trường hợp sử dụng không thêm nhiều giá trị"? Đó là một ý kiến ​​chủ quan, nó có thể cung cấp cho tôi rất nhiều giá trị!
Jon Kruger

34
Điều làm tôi bực mình nhất khi không có tính năng này, là không thể làm những thứ như [PropertyReference (x => x.SomeProperty)]. Thay vào đó, bạn cần chuỗi ma thuật và typeof (), mà tôi nghĩ là loại hút.
Asbjørn Ulsberg

13
@ John: Tôi nghĩ bạn đánh giá rất thấp chi phí thiết kế, chỉ định, thực hiện và thử nghiệm một tính năng ngôn ngữ mới.
Jon Skeet

14
Tôi chỉ muốn thêm vào lời biện hộ của @ Timwi rằng đây không phải là nơi duy nhất họ đang được thảo luận và quan điểm 13K về câu hỏi này hàm ý mức độ quan tâm lành mạnh. Ngoài ra: cảm ơn vì đã nhận được một câu trả lời có thẩm quyền, Jon.
Jordan Gray

84

Một thuộc tính trang trí một lớp tại thời gian biên dịch, nhưng một lớp chung không nhận được thông tin loại cuối cùng cho đến khi thời gian chạy. Vì thuộc tính có thể ảnh hưởng đến quá trình biên dịch, nó phải "hoàn thành" tại thời điểm biên dịch.

Xem bài viết MSDN này để biết thêm thông tin.


3
Bài báo nói rằng họ không thể, nhưng không có lý do. Tôi khái niệm hiểu câu trả lời của bạn. Bạn có biết thêm tài liệu chính thức nào về vấn đề này không?
Bryan Watts

2
Bài viết này đề cập đến thực tế là IL vẫn chứa các trình giữ chỗ chung được thay thế bằng loại thực tế khi chạy. Phần còn lại đã được tôi suy luận ... :)
GalacticCowboy

1
Đối với giá trị của nó, VB thực thi cùng một ràng buộc: "Các lớp chung hoặc chứa trong một loại chung không thể kế thừa từ một lớp thuộc tính."
GalacticCowboy

1
ECMA-334, phần 14.16 nói "Các biểu thức không đổi được yêu cầu trong các ngữ cảnh được liệt kê bên dưới và điều này được biểu thị trong ngữ pháp bằng cách sử dụng biểu thức không đổi. Trong các bối cảnh này, lỗi thời gian biên dịch xảy ra nếu một biểu thức không thể được đánh giá đầy đủ khi biên dịch thời gian." Các thuộc tính có trong danh sách.
GalacticCowboy

4
Điều này dường như bị mâu thuẫn bởi một câu trả lời khác nói rằng IL sẽ cho phép nó. ( stackoverflow.com/a/294259/3195477 )
UuDdLrLrSs

22

Tôi không biết tại sao nó không được phép, nhưng đây là một cách giải quyết có thể

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}


[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}

3
Thật không may, bạn mất thời gian biên dịch khi sử dụng thuộc tính. Hãy tưởng tượng thuộc tính tạo ra một cái gì đó của loại chung. Bạn có thể làm việc xung quanh nó, nhưng nó sẽ tốt đẹp; đó là một trong những điều trực quan mà bạn ngạc nhiên bạn không thể làm, như phương sai (hiện tại).
Bryan Watts

14
Đáng buồn là cố gắng không làm điều này là lý do tại sao tôi tìm thấy câu hỏi SO này. Tôi đoán tôi sẽ phải gắn bó với việc đối phó với typeof. Nó thực sự cảm thấy ilke một từ khóa bẩn bây giờ sau khi thuốc generic đã tồn tại quá lâu bây giờ.
Chris Marisic

13

Điều này không thực sự chung chung và bạn vẫn phải viết lớp thuộc tính cụ thể cho mỗi loại, nhưng bạn có thể sử dụng giao diện cơ sở chung để mã hóa một chút phòng thủ, viết mã ít hơn so với yêu cầu khác, nhận được lợi ích của đa hình, v.v.

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}

8

Đây là một câu hỏi rất hay. Theo kinh nghiệm của tôi với các thuộc tính, tôi nghĩ rằng các hạn chế được đặt ra bởi vì khi suy niệm về một thuộc tính nó sẽ tạo ra một tình trạng mà trong đó bạn sẽ phải kiểm tra tất cả các hoán vị loại thể: typeof(Validates<string>), typeof(Validates<SomeCustomType>), vv ...

Theo tôi, nếu yêu cầu xác thực tùy chỉnh tùy thuộc vào loại, một thuộc tính có thể không phải là cách tiếp cận tốt nhất.

Có lẽ một lớp xác nhận lấy một SomeCustomValidationDelegatehoặc ISomeCustomValidatormột tham số sẽ là một cách tiếp cận tốt hơn.


Tôi đồng ý với bạn. Tôi đã có câu hỏi này trong một thời gian dài và hiện đang xây dựng một hệ thống xác nhận. Tôi đã sử dụng thuật ngữ hiện tại của mình để đặt câu hỏi, nhưng không có ý định thực hiện một cách tiếp cận dựa trên cơ chế này.
Bryan Watts

Tôi tình cờ phát hiện ra điều này trong khi thực hiện một thiết kế cho cùng một mục tiêu: xác nhận. Tôi đang cố gắng thực hiện theo cách dễ dàng để phân tích cả hai cách tự động (nghĩa là bạn có thể tạo một báo cáo mô tả xác nhận trong ứng dụng để xác nhận) và bằng cách con người hình dung mã. Nếu không phải thuộc tính, tôi không chắc giải pháp tốt nhất sẽ là gì ... Tôi vẫn có thể thử thiết kế thuộc tính, nhưng khai báo thủ công thuộc tính loại cụ thể. Đó là một công việc nhiều hơn một chút, nhưng mục đích là cho độ tin cậy của việc biết các quy tắc xác nhận (và có thể báo cáo về chúng để xác nhận).
bambams

4
bạn có thể kiểm tra đối với các định nghĩa loại chung (nghĩa là typeof (Xác thực <>)) ...
Melvyn

5

Đây hiện không phải là một tính năng ngôn ngữ C #, tuy nhiên có nhiều thảo luận về repo ngôn ngữ C # chính thức .

Từ một số ghi chú cuộc họp :

Mặc dù điều này sẽ hoạt động trên nguyên tắc, có một số lỗi trong hầu hết các phiên bản của thời gian chạy để nó không hoạt động chính xác (nó không bao giờ được thực hiện).

Chúng ta cần một cơ chế để hiểu thời gian chạy đích mà nó hoạt động. Chúng tôi cần điều đó cho nhiều thứ, và hiện đang xem xét điều đó. Cho đến lúc đó, chúng tôi không thể lấy nó.

Ứng cử viên cho một phiên bản C # chính, nếu chúng tôi có thể tạo đủ số lượng phiên bản thời gian chạy đối phó với nó.


1

Cách giải quyết của tôi là một cái gì đó như thế này:

public class DistinctType1IdValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type1> validator;

    public DistinctIdValidation()
    {
        validator = new DistinctValidator<Type1>(x=>x.Id);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

public class DistinctType2NameValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type2> validator;

    public DistinctType2NameValidation()
    {
        validator = new DistinctValidator<Type2>(x=>x.Name);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }

[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }
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.