C #이 일반 속성 유형을 금지하는 이유는 무엇입니까?


510

컴파일 타임 예외가 발생합니다.

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

C #이 일반 속성을 지원하지 않는다는 것을 알고 있습니다. 그러나 많은 인터넷 검색 후 이유를 찾을 수없는 것 같습니다.

왜 일반 형식을 파생시킬 수 없는지 아는 사람이 Attribute있습니까? 어떤 이론?


17
당신은 할 수 있습니다 [Validates (typeof (string)]-제네릭이 더 좋을 것이라는 데 동의합니다.
ConsultUtah

20
문제는이 질문에 매우 늦게 또한이 있지만, 그것은뿐만 아니라 다음과 같이 자신뿐만 아니라 allwed되지 않습니다 (분명히 어쨌든 속성으로 인스턴스화 할 수 없습니다) 추상적 인 속성 클래스 속성 슬픈이다 : abstract class Base<T>: Attribute {}비 만드는 데 사용할 수있는 이와 같은 일반적인 파생 클래스 :class Concrete: Base<MyType> {}
Lucero

88
람다를 허용하는 일반적인 속성과 속성을 원합니다. 같은 일을 상상 [DependsOnProperty<Foo>(f => f.Bar)]이나 [ForeignKey<Foo>(f => f.IdBar)]...
제이 섹 고르곤에게

3
이것은 내가 방금 겪은 상황에서 매우 유용 할 것입니다. 제네릭 형식을 허용하고 지정된 실제 값에 해당 형식을 적용하는 LinkedValueAttribute를 만드는 것이 좋습니다. 이 열거 형 값을 선택하면 사용해야하는 다른 열거 형의 "기본"값을 열거 형에 사용할 수 있습니다. 이러한 속성 중 여러 유형을 다른 유형에 지정할 수 있으며 필요한 유형에 따라 필요한 값을 얻을 수 있습니다. Type과 Object를 사용하도록 설정할 수 있지만 강력하게 입력하면 큰 도움이됩니다.
KeithS

10
약간의 IL을 신경 쓰지 않으면 유망한 것처럼 보입니다 .
Jarrod Dixon

답변:


358

글쎄, 왜 사용할 수 없는지 대답 할 수는 없지만 CLI 문제가 아니라는 것을 확인할 수 있습니다 . CLI 스펙은 (내가 볼 수있는 한) 언급하지 않으며 IL을 직접 사용하는 경우 일반 속성을 만들 수 있습니다. 이를 금지하는 C # 3 스펙의 일부-섹션 10.1.4 "클래스 기본 스펙"은 정당성을 나타내지 않습니다.

주석이 달린 ECMA C # 2 사양은 허용되지 않는 것에 대한 예를 제공하지만 유용한 정보를 제공하지 않습니다.

주석이 달린 C # 3 사양의 사본이 내일 도착해야합니다. 자세한 정보가 있는지 확인해 보겠습니다. 어쨌든 런타임 결정이 아닌 언어 결정입니다.

편집 : Eric Lippert의 답변 (그림으로 표시) : 많은 가치를 추가하지 않는 유스 케이스의 언어와 컴파일러의 복잡성을 피하는 것을 제외하고 특별한 이유는 없습니다.


139
"언어와 컴파일러 모두 피할 복잡성 제외"... 우리를주는 사람들이 공동 및 contravariance에서 ... 있음
FLQ

254
"많은 가치를 부여하지 않는 유스 케이스"? 그것은 주관적인 의견이며, 그것은 나에게 많은 가치를 제공 할 수 있습니다!
Jon Kruger

34
이 기능이없는 것에 대해 가장 귀찮게하는 것은 [PropertyReference (x => x.SomeProperty)]와 같은 작업을 수행 할 수 없다는 것입니다. 대신 마술 문자열과 typeof ()가 필요합니다.
Asbjørn Ulsberg

13
@John : 새로운 언어 기능을 디자인, 지정, 구현 및 테스트하는 비용을 과소 평가한다고 생각합니다.
Jon Skeet

14
나는 이것이 @Timwi의 방어에 덧붙여서 이것이 논의되고 있는 유일한 장소는 아니며 ,이 질문에 대한 13K 견해는 건강한 수준의 관심을 암시합니다. 또한 정식 답변을 해주셔서 감사합니다. Jon.
Jordan Gray

84

속성은 컴파일 타임에 클래스를 장식하지만 일반 클래스는 런타임까지 최종 유형 정보를받지 않습니다. 이 속성은 컴파일에 영향을 줄 수 있으므로 컴파일시 "완료"해야합니다.

자세한 내용은이 MSDN 기사 를 참조하십시오.


3
이 기사는 불가능하지만 이유가 없다고 주장합니다. 나는 당신의 대답을 개념적으로 이해합니다. 이 문제에 대한 공식 문서가 더 있습니까?
Bryan Watts

2
이 기사에서는 IL에 실제 유형으로 대체되는 일반 자리 표시자가 여전히 런타임에 포함되어 있다는 사실을 다루고 있습니다. 나머지는 나에 의해 추론되었다 ... :)
GalacticCowboy

1
VB는 "제네릭 형식에 포함되거나 제네릭 형식에 포함 된 클래스는 특성 클래스에서 상속 할 수 없습니다."라는 동일한 제약 조건을 적용합니다.
GalacticCowboy

1
ECMA-334, 섹션 14.16은 "아래에 설명 된 상황에서 상수 표현식이 필요하며 상수 표현식을 사용하여 문법으로 표시됩니다. 이러한 상황에서 컴파일시 오류가 발생할 수 있습니다. 시각." 속성이 목록에 있습니다.
GalacticCowboy

4
이것은 IL이 허용 할 것이라는 또 다른 대답과 모순되는 것 같습니다. ( stackoverflow.com/a/294259/3195477 )
UuDdLrLrSs

22

왜 허용되지 않는지 모르겠지만 가능한 해결 방법 중 하나입니다.

[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
불행히도 속성을 사용할 때 컴파일 타임 입력이 손실됩니다. 속성이 일반 유형의 무언가를 생성한다고 상상해보십시오. 당신은 그것을 해결할 수 있지만 좋을 것입니다. 그것은 분산 (현재)과 같이 당신이 할 수없는 놀랍게도 직관적 인 것들 중 하나입니다.
Bryan Watts

14
슬프게도 이것을하지 않으려 고 노력하는 이유는이 SO 질문을 찾았습니다. 나는 typeof를 다루는 것을 고수해야 할 것 같습니다. 이제 제네릭이 오랫동안 존재 해 온 이후로 더티 키워드가 실제로 느껴집니다.
Chris Marisic

13

이것은 실제로 일반적인 것은 아니며 여전히 유형별로 특정 속성 클래스를 작성해야하지만, 일반적인 기본 인터페이스를 사용하여 조금 방어 적으로 코딩하고, 필요한 것보다 적은 코드를 작성하고, 다형성의 이점을 얻을 수 있습니다.

//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

이것은 매우 좋은 질문입니다. 속성 내 경험에, 나는 속성에 반영 때 가능한 모든 유형의 순열을 확인해야하는 조건을 만들 것이기 때문에 제약 조건이 장소에 생각 : typeof(Validates<string>), typeof(Validates<SomeCustomType>), 등 ...

제 생각에는 유형에 따라 사용자 지정 유효성 검사가 필요한 경우 속성이 최선의 방법이 아닐 수도 있습니다.

a SomeCustomValidationDelegate또는 ISomeCustomValidatora를 매개 변수로 사용 하는 유효성 검사 클래스가 더 나은 방법 일 수 있습니다.


동의합니다. 나는이 질문을 오랫동안 해왔으며 현재 유효성 검사 시스템을 구축 중입니다. 나는 현재의 용어를 사용하여 질문을했지만이 메커니즘을 기반으로 한 접근법을 구현할 의도는 없습니다.
Bryan Watts

나는 같은 목표를위한 디자인 작업을하는 동안이 문제를 우연히 발견했다 : 검증. 자동으로 분석하기 쉬운 방법으로 시도하고 있습니다 (즉, 확인을 위해 응용 프로그램에서 유효성 검사를 설명하는 보고서를 생성 할 수 있음)와 사람이 코드를 시각화하여 수행하려고합니다. 속성이 아닌 경우 최상의 솔루션이 무엇인지 잘 모르겠습니다. 여전히 속성 디자인을 시도하지만 유형별 속성을 수동으로 선언합니다. 좀 더 많은 작업이지만 목표는 유효성 검사 규칙을 알고 (확인을 위해 규칙을보고 할 수 있음) 신뢰성을 높이는 것입니다.
bambams

4
당신은 일반적인 타입 정의를 검사 할 수 있습니다 (즉 typeof (Validates <>)) ...
Melvyn

5

이것은 현재 C # 언어 기능은 아니지만 공식 C # 언어 저장소에 대한 많은 토론이 있습니다 .

에서 일부 회의 노트 :

이것이 원칙적으로 작동하더라도 대부분의 런타임 버전에는 버그가 있으므로 제대로 작동하지 않습니다 (실행되지 않았습니다).

작동하는 대상 런타임을 이해하는 메커니즘이 필요합니다. 우리는 많은 것들을 위해 그것을 필요로하며 현재 그것을보고 있습니다. 그때까지는 취할 수 없습니다.

충분한 수의 런타임 버전을 처리 할 수있는 경우 주요 C # 버전의 후보입니다.


1

내 해결 방법은 다음과 같습니다.

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; }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.