문제

양식을 관리 할 ASP.NET 페이지를 작성했습니다. 그들은 다음 기본 클래스를 기반으로합니다.

public abstract class FormPageBase<TInterface, TModel> : Page, IKeywordProvider 
        where TModel:ActiveRecordBase<MasterForm>, TInterface, new()
        where TInterface:IMasterForm
    {
        public TInterface FormData { get; set; }                   
     }

샘플 서브 클래스가 여기에 있습니다.

public partial class PersonalDataFormPage : FormPageBase<IPersonalDataForm, PersonalDataForm>, IHasFormData<IPersonalDataForm>, IHasContact
    {
    }

아래에는 페이지에서 "formdata"를 "소비"하여 읽거나 쓸 수 있도록 USERCONTROL이 있습니다.

그런 다음 모든 양식 서브 클래스의 기본 인터페이스에서 작동하고 싶은 "일반적인"사용자 컨트롤이 있습니다 ... Imasterform

그러나 UserControl이 캐스팅 페이지를 시도 할 때 (페이지를 주조하려고 시도했을 때 IHasFormData<IMasterForm> 페이지가 있다고 알려줍니다 IHasFormData<IFormSubclass> iformsubclass에 제약이 있어도 imasterform이라는 제약이 있습니다.

어쨌든 제네릭 서브 클래스에서 제네릭 슈퍼 클래스로 캐스트 할 수 있는가?

public abstract class FormControlBase<T> : UserControl, IKeywordProvider
    where T:IMasterForm 
{

    protected T FormData { get; set; }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

//This cast is failing when my common control's T does not exactly match
// the T of the Page.. even though the common controls TInterface is a base interface to the
//pages TInterface

        FormData = ((IHasFormData<T>) Page).FormData;

        if (!IsPostBack)
        {
            PopulateBaseListData();
            BindDataToControls();
        }
    }

    protected abstract void PopulateBaseListData();
    protected abstract void BindDataToControls();


    public abstract void SaveControlsToData();


    #region IKeywordProvider
    public List<IKeyword> GetKeywords(string categoryName)
    {
        if(!(Page is IKeywordProvider ))
            throw new InvalidOperationException("Page is not  IKeywordProvider");

        return ((IKeywordProvider) Page).GetKeywords(categoryName);
    }

    #endregion

}
도움이 되었습니까?

해결책

이 복잡한 문제를 더 간결하게 재현 할 수 있는지 먼저 살펴 보겠습니다. 일반적인 인터페이스가 있습니다 IHasFormData<T>. 구현하는 것으로 알려진 객체가 있습니다 IHasFormData<IFormSubclass>. 당신은 그것을 변환하고 싶습니다 IHasFormData<IMasterForm>. iformsubclass에서 imasterform으로 참조 변환이 있음을 알고 있습니다. 이것은 실패합니다.

예?

그것이 문제에 대한 올바른 진술이라면, 그렇습니다. 이것은 인터페이스 공분산의 문제입니다. C# 3은 인터페이스 공분산을 지원하지 않습니다. C# 4 Will, 컴파일러에게 공분산이 안전하다는 것을 증명할 수 있다면.

왜 이것이 안전하지 않은지 간단히 설명하겠습니다. 분명한 서브 클래스 관계가있는 사과, 오렌지, 과일 수업이 있다고 가정 해 봅시다. 당신은 있습니다 IList<Apple> 캐스팅하고 싶은 IList<Fruit>. 이 공변량 전환은 C# 4에서 합법적이지 않으며 안전하지 않기 때문에 합법적 일 수 없습니다. 우리가 그것을 허용했다고 가정 해 봅시다. 그런 다음 다음을 수행 할 수 있습니다.

IList<Apple> apples = new List<Apple>();
IList<Fruit> fruits = apples;
fruits.Add(new Orange()); 
// We just put an orange into a list of apples!  
// And now the runtime crashes.

문제는 그 사실입니다 List<T> T를 인수로 취하는 메소드를 노출시킵니다. 컴파일러가 인터페이스에서 공분산 변환을 허용하려면 IHasFormData<T>, 당신은 컴파일러에게 그것을 증명해야합니다 IHasFormData<T> t를 논쟁으로 취하는 것은 아무것도 드러납니다. 인터페이스를 선언하여 그렇게 할 것입니다 IHasFormData<out T>, "t는 출력 위치에만 나타난다"는 니모닉을 의미합니다. 그런 다음 컴파일러는 귀하의 주장이 올바른지 확인하고 공분산 변환을 허용합니다.

C# 4 의이 기능에 대한 자세한 내용은 기능 디자인에 대한 메모 아카이브를 참조하십시오.

http://blogs.msdn.com/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

다른 팁

4.0 이전의 C#은 유형 매개 변수와 정확히 일치하도록 모든 캐스트를 일반 유형으로 요구합니다. 4.0은 공동 및 콘트라 분산을 소개하지만, 당신이 수행하려는 캐스트는 이전 버전에서는 불가능합니다.

나는 당신과 매우 비슷한 기본 페이지를 가지고 있습니다. 이것이 내가 내 것을 정의하는 방법입니다.

public abstract class ViewBasePage<TPresenter, TView> : Page, IView
        where TPresenter : Presenter<TView>
        where TView : IView
{
    protected TPresenter _presenter;

    public TPresenter Presenter
    {
        set
        {
            _presenter = value;
            _presenter.View = (TView) ((IView) this);
        }
}

나는 당신이 같은 것이 필요하다고 생각합니다 FormData = ((IHasFormData<T>) (IMasterForm )Page)).FormData;

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top