developer tip

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

optionbox 2020. 10. 21. 07:57
반응형

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


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

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

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

원하는 것은 반환 유형 공분산처럼 들립니다. 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;
}

이 블로그 게시물 시리즈에서는 반환 유형 공분산 문제에 대해 자세히 설명하고 IL로 수행하는 방법에 대해서도 설명합니다.

http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/14/93495.aspx

http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/16/93516.aspx

http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/19/93562.aspx

링크를 게시하는 것만으로도 미안하지만 매우 상세하며 여기에서 스 니펫을 인용하는 것은 그다지 도움이되지 않습니다. IL 코드를 사용하여 달성 할 수있는 방법을 보여줍니다.


이것을 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();

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


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

YourPageType myPage = (YourPageType)yourControl.Page;

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

You can make your page implement some custom interface that exposes a method called "GetContext" or something, and it returns your specific information. Then your control can simply request the page and cast:

var myContextPage = this.Page as IMyContextGetter;

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

Then you can use that context however you wish.


You can access your page from any control by walking up the parent tree. That is

myParent = this;

while(myParent.parent != null)
  myParent = myParent.parent;

*Did not compile or test.

Or get the parent page in the current context (depends on your version).


Then what I like to do is this: I create an interface with the functions I want to use in the control (for example IHostingPage)

Then I cast the parent page 'IHostingPage host = (IHostingPage)Parent;' and I am all set to call the function on the page I need from my control.


I will do it in this way:

class R {
    public int A { get; set; }
}

class R1: R {
    public int B { get; set; }
}

class A
{        
    public R X { get; set; }
}

class B : A 
{
    private R1 _x;
    public new R1 X { get => _x; set { ((A)this).X = value; _x = value; } }
}

참고URL : https://stackoverflow.com/questions/5709034/does-c-sharp-support-return-type-covariance

반응형