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.