[c#] C #은 반환 형식 공분산을 지원합니까?

저는 .NET 프레임 워크로 작업하고 있으며 모든 웹 사이트에서 사용하는 사용자 지정 유형의 페이지를 만들고 싶습니다. 컨트롤에서 페이지에 액세스하려고 할 때 문제가 발생합니다. 기본 페이지 대신 특정 유형의 페이지를 반환하고 싶습니다. 이것을 할 방법이 있습니까?

public class MyPage : Page
{
    // My own logic
}

public class MyControl : Control
{
    public MyPage Page { get; set; }
}



답변


업데이트 :이 답변은 2011 년에 작성되었습니다. 20 년의 사람들이 C #에 대한 반환 형식 공분산을 제안한 후 마침내 구현 될 것으로 보입니다. 나는 오히려 놀랐다. 공지 사항 은 https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/ 하단을 참조하십시오 . 세부 사항이 뒤따를 것이라고 확신합니다.


원하는 것은 반환 유형 공분산 인 것처럼 들립니다. C #은 반환 형식 공분산을 지원하지 않습니다.

반환 유형 공분산은보다 구체적인 유형을 반환하는 유형으로 덜 구체적인 유형을 반환하는 기본 클래스 메서드를 재정의하는 곳입니다.

abstract class Enclosure
{
    public abstract Animal Contents();
}
class Aquarium : Enclosure
{
    public override Fish Contents() { ... }
}

Enclosure를 통한 콘텐츠 소비자는 동물을 기대하고 Aquarium은 그 요구 사항을 충족 할뿐만 아니라 동물은 항상 물고기라는 더 엄격한 약속을 약속하기 때문에 안전합니다.

이러한 종류의 공분산은 C #에서 지원되지 않으며 지원되지 않을 것입니다. CLR에서 지원하지 않습니다. (C ++ 및 CLR의 C ++ / CLI 구현에 의해 지원됩니다. 아래에서 제안하는 것과 같은 마법의 도우미 메서드를 생성하여 지원합니다.)

(일부 언어는 형식 매개 변수 유형 반 변성도 지원합니다. 즉, Animal을 취하는 메소드로 Fish를 취하는 메소드를 재정의 할 수 있습니다. 다시 말하지만, 계약이 이행됩니다. 기본 클래스는 모든 Fish를 처리해야하며 클래스는 물고기뿐만 아니라 모든 동물을 처리 할 것을 약속합니다. 마찬가지로 C # 및 CLR은 형식 매개 변수 유형 반 변성을 지원하지 않습니다.)

이 제한을 해결할 수있는 방법은 다음과 같은 작업을 수행하는 것입니다.

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    protected override Animal GetContents() { return this.Contents(); }
    public new Fish Contents() { ... }
}

이제 가상 메서드를 재정의하는 이점과 컴파일 타임 유형 Aquarium을 사용할 때 더 강력한 타이핑을 얻을 수 있습니다.


답변

인터페이스를 사용하여 인터페이스를 명시 적으로 구현하여 해결했습니다.

public interface IFoo {
  IBar Bar { get; }
}
public class Foo : IFoo {
  Bar Bar { get; set; }
  IBar IFoo.Bar => Bar;
}


답변

이것을 MyControl 개체에 배치하면 작동합니다.

 public new MyPage Page {get return (MyPage)Page; set;}'

다른 유형을 반환하기 때문에 속성을 재정의 할 수 없지만 재정의 할 수 있습니다.

이 예에서는 비교적 간단하기 때문에 공분산이 필요하지 않습니다. 에서 기본 개체 Page를 상속하는 것뿐입니다 MyPage. 모든 Control반환 할 것을 MyPage대신 Page요구는 재정의 할 수 Page의 속성을Control


답변

예, 공분산을 지원하지만 달성하려는 정확한 것에 따라 다릅니다.

나는 또한 제네릭을 많이 사용하는 경향이 있습니다. 즉, 다음과 같은 작업을 할 때 의미합니다.

class X<T> {
    T doSomething() {
    }

}

class Y : X<Y> {
    Y doSomethingElse() {
    }
}

var Y y = new Y();
y = y.doSomething().doSomethingElse();

그리고 당신의 유형을 “잃지”않는 것.


답변

이 기능은 곧 출시 될 C # 9.0 (.Net 5) 의 기능으로 지금 미리보기 버전을 다운로드 할 수 있습니다 .

다음 코드는 현재 (주지 않고 성공적으로 빌드 error CS0508: 'Tiger.GetFood()': return type must be 'Food' to match overridden member 'Animal.GetFood()')

class Food { }
class Meat : Food { }

abstract class Animal {
    public abstract Food GetFood();
}

class Tiger : Animal {
    public override Meat GetFood() => default;
}

class Program {
    static void Main() => new Tiger();
}


답변

나는 그것을 시도하지 않았지만 작동하지 않습니까?

YourPageType myPage = (YourPageType)yourControl.Page;


답변

예. 이를 수행하는 방법은 여러 가지가 있으며 이는 하나의 옵션 일뿐입니다.

페이지에서 “GetContext”라는 메서드를 노출하는 일부 사용자 지정 인터페이스를 구현하고 특정 정보를 반환하도록 만들 수 있습니다. 그런 다음 컨트롤은 단순히 페이지를 요청하고 캐스트 할 수 있습니다.

var myContextPage = this.Page as IMyContextGetter;

if(myContextPage != null)
   var myContext = myContextPage.GetContext();

그런 다음 원하는대로 해당 컨텍스트를 사용할 수 있습니다.