C # 컴파일러가 다른 기본 클래스에서 파생 될 때 "유형이 통합 될 수있다"고 불평하는 이유는 무엇입니까?
내 현재 비 컴파일 코드는 다음과 유사합니다.
public abstract class A { }
public class B { }
public class C : A { }
public interface IFoo<T>
{
void Handle(T item);
}
public class MyFoo<TA> : IFoo<TA>, IFoo<B>
where TA : A
{
public void Handle(TA a) { }
public void Handle(B b) { }
}
C # 컴파일러는 다음 규칙 / 오류를 인용하여이를 컴파일하지 않습니다.
'MyProject.MyFoo <TA>'는 'MyProject.IFoo <TA>'및 'MyProject.IFoo <MyProject.B>'를 모두 구현할 수 없습니다. 일부 형식 매개 변수 대체를 위해 통합 될 수 있기 때문입니다.
이 오류의 의미를 이해합니다. TA
어떤 것이 든 가능 하다면 기술적으로는 B
두 가지 다른 Handle
구현에 대한 모호성을 도입 할 수도 있습니다 .
그러나 TA 는 아무것도 될 수 없습니다 . 유형 계층 구조를 기반으로 TA
할 수 일 B
- 적어도, 나는하지 않는 생각 이 있습니다. TA
에서 파생되어야합니다 A
않는, 하지 에서 파생 B
, 분명히 C의 # /. NET에는 여러 클래스 상속이 없습니다.
나는 일반 매개 변수를 제거하고 교체하는 경우 TA
에 C
, 또는 A
, 그것은 컴파일합니다.
그렇다면 왜이 오류가 발생합니까? 컴파일러의 버그 또는 일반적인 비 인텔리전스입니까, 아니면 내가 놓친 다른 것이 있습니까?
해결 방법이 있습니까? 아니면 MyFoo
가능한 모든 단일 TA
파생 형식에 대해 제네릭 클래스를 별도의 비 제네릭 클래스로 다시 구현해야 합니까?
이는 다음과 같은 C # 4 사양의 섹션 13.4.2의 결과입니다.
C에서 생성 된 가능한 생성 된 형식이 형식 인수가 L로 대체 된 후 L의 두 인터페이스가 동일 해지면 C 선언이 유효하지 않습니다. 가능한 모든 생성 된 형식을 결정할 때 제약 조건 선언은 고려되지 않습니다.
두 번째 문장에 유의하십시오.
따라서 컴파일러의 버그가 아닙니다. 컴파일러가 정확합니다. 언어 사양의 결함이라고 주장 할 수 있습니다.
일반적으로 제네릭 유형에 대한 사실을 추론해야하는 거의 모든 상황에서 제약 조건이 무시됩니다. 제약 조건은 대부분 제네릭 형식 매개 변수 의 효과적인 기본 클래스 를 결정하는 데 사용되며 그 밖의 사항은 거의 없습니다.
불행히도, 그것은 때때로 당신이 발견 한 것처럼 언어가 불필요하게 엄격한 상황으로 이어집니다.
일반적으로 "동일한"인터페이스를 두 번 구현하는 것은 일반적으로 나쁜 코드 냄새입니다. 어떤 방식 으로든 제네릭 형식 인수로만 구별됩니다. 그것은 예를 들어 가지고, 기괴한 class C : IEnumerable<Turtle>, IEnumerable<Giraffe>
C가 모두 거북이의 순서이라고 무엇인가 - 와 , 기린의 순서 를 동시에 ? 여기서 수행하려는 실제 작업을 설명 할 수 있습니까? 실제 문제를 해결하기위한 더 나은 패턴이있을 수 있습니다.
실제로 인터페이스가 설명과 정확히 일치하는 경우 :
interface IFoo<T>
{
void Handle(T t);
}
그런 다음 인터페이스의 다중 상속은 또 다른 문제를 제시합니다. 이 인터페이스를 반 변적으로 만들기로 합리적으로 결정할 수 있습니다.
interface IFoo<in T>
{
void Handle(T t);
}
이제 당신이 가지고 있다고 가정
interface IABC {}
interface IDEF {}
interface IABCDEF : IABC, IDEF {}
과
class Danger : IFoo<IABC>, IFoo<IDEF>
{
void IFoo<IABC>.Handle(IABC x) {}
void IFoo<IDEF>.Handle(IDEF x) {}
}
그리고 이제 상황이 정말 미쳐 ...
IFoo<IABCDEF> crazy = new Danger();
crazy.Handle(null);
어떤 Handle 구현이 호출 되는지 ???
이 문제에 대한 자세한 내용은이 기사와 의견을 참조하십시오.
Microsoft Connect에서 논의한 것처럼 의도적으로 설계된 것 같습니다.
해결 방법은 다른 인터페이스를 다음과 같이 정의하는 것입니다.
public interface IIFoo<T> : IFoo<T>
{
}
그런 다음 대신 다음과 같이 구현하십시오.
public class MyFoo<TA> : IIFoo<TA>, IFoo<B>
where TA : A
{
public void Handle(TA a) { }
public void Handle(B b) { }
}
이제 mono로 잘 컴파일됩니다 .
기본적으로 동일한 질문에 대한 내 답변을 참조하십시오 : https://stackoverflow.com/a/12361409/471129
어느 정도는 할 수 있습니다! 유형을 제한하는 한정자 대신 차별화 방법을 사용합니다.
It does not unify, in fact it might be better than if it did because you can tease the separate interfaces apart.
See my post here, with a fully working example in another context. https://stackoverflow.com/a/12361409/471129
Basically, what you do is add another type parameter to IIndexer, so that it become IIndexer <TKey, TValue, TDifferentiator>
.
Then you when you use it twice, you pass "First" to the 1st use, and "Second" for the 2nd use
So, class Test becomes: class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>
Thus, you can do new Test<int,int>()
where First, and Second are trivial:
interface First { }
interface Second { }
I know it has been a while since the thread was posted but for those who come down to this thread via the search engine for help. Note that 'Base' stands for base class for TA and B in below.
public class MyFoo<TA> : IFoo<Base> where TA : Base where B : Base
{
public void Handle(Base obj)
{
if(obj is TA) { // TA specific codes or calls }
else if(obj is B) { // B specific codes or calls }
}
}
Taking a guess now...
Couldn't A, B and C be declared in outside assemblies, where the type hierarchy may change after the compilation of MyFoo<T>, bringing havoc into the world?
The easy workaround is just to implement Handle(A) instead of Handle(TA) (and use IFoo<A> instead of IFoo<TA>). You cant do much more with Handle(TA) than access methods from A (due to the A : TA constraint) anyway.
public class MyFoo : IFoo<A>, IFoo<B> {
public void Handle(A a) { }
public void Handle(B b) { }
}
Hmm, what about this:
public class MyFoo<TA> : IFoo<TA>, IFoo<B>
where TA : A
{
void IFoo<TA>.Handle(TA a) { }
void IFoo<B>.Handle(B b) { }
}
You can sneak it under the radar if you put one interface on a base class.
public interface IFoo<T> {
}
public class Foo<T> : IFoo<T>
{
}
public class Foo<T1, T2> : Foo<T1>, IFoo<T2>
{
}
'programing tip' 카테고리의 다른 글
Maven : 저장소 요소가 distributionManagement 내부의 POM에 지정되지 않았습니다. (0) | 2020.10.25 |
---|---|
Google API 인텔 x86 아톰과 Google Play 인텔 x86 아톰 시스템 이미지의 차이점 (0) | 2020.10.25 |
Linux oom-killer 로그 이해 (0) | 2020.10.25 |
항상 백그라운드에서 서비스를 실행하는 방법은 무엇입니까? (0) | 2020.10.25 |
.net 서비스 버스 권장 사항? (0) | 2020.10.25 |