هل هذه مشكلة التباين؟ لست متأكدا إذا جدار الطوب
-
18-09-2019 - |
سؤال
كتبت صفحات ASP.NET التي ستدير النماذج. إنهم يعتمدون على الفئة الأساسية التالية.
public abstract class FormPageBase<TInterface, TModel> : Page, IKeywordProvider
where TModel:ActiveRecordBase<MasterForm>, TInterface, new()
where TInterface:IMasterForm
{
public TInterface FormData { get; set; }
}
و Subclass عينة هنا:
public partial class PersonalDataFormPage : FormPageBase<IPersonalDataForm, PersonalDataForm>, IHasFormData<IPersonalDataForm>, IHasContact
{
}
أدناه لدي UserControl على الصفحة التي أريد "تستهلك" "FormData" من الصفحة حتى تتمكن من القراءة / الكتابة إليها.
أنا آنذا، لديك عنصر تحكم مستخدم "مشترك" الذي أريد أن أقوم بالعمل على الواجهة الأساسية لجميع الفئات الفرعية النموذجية ... ImasterForm
ولكن عندما يحاول UserControl page page.formdata (بعد أن حاول أن يلقي الصفحة IHasFormData<IMasterForm>
يخبرني أن الصفحة هي IHasFormData<IFormSubclass>
على الرغم من أن لدي قيود على iformsubclass التي تقول إنها أيضا imasterform
هل هناك على أي حال يمكنني تناوله من الفئة الفرعية العامة إلى Superclass العامة أو هو هذا "التباين" وشيء C # 4.0؟
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>
. وبعد أنت تعرف أن هناك تحويل مرجعي من IFORMSUSCCLASS إلى ImasterForm. هذا فشل.
نعم؟
إذا كان هذا هو عبارة صحيحة للمشكلة، فهذا نعم، فهذه مسألة التباين الواجهة. C # 3 لا يدعم واجهة التباين. C # 4 سوف، إذا كنت تستطيع إثبات التحويل البرمجي أن التباين الآمن.
اسمحوا لي أن أصف لك لفترة وجيزة لماذا قد لا يكون هذا آمنا. لنفترض أن لديك فصول أبل والبرتقال والفواكه مع العلاقات الفيزيائية الواضحة الواضحة. انت تملك IList<Apple>
الذي ترغب في طرحه IList<Fruit>
. وبعد هذا التحويل Covariant ليس قانونيا في 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>
يكشف الطريقة التي تأخذها حجة. من أجل التحويل البرمجي للسماح بتحويلات Covariant على واجهتك IHasFormData<T>
, ، يجب أن تثبت إلى المحول البرمجي ذلك IHasFormData<T>
يعرض أي شيء يأخذ حجة. سوف تفعل ذلك بإعلان الواجهة IHasFormData<out T>
, ، معنى الذاكرة الهرمية "يظهر فقط في مواقع الإخراج". بعد ذلك، سيحقق المحول البرمجي أن مطالبتك صحيحة، وابدأ في السماح بتحويلات Covariant.
لمزيد من المعلومات حول هذه الميزة في C # 4، راجع أرشيف ملاحظات على تصميم الميزة:
http://blogs.msdn.com/ericlippert/archive/tags/covariance+and+contravariace/default.aspx.
نصائح أخرى
C # قبل 4.0 يتطلب جميع إلقاء الأنواع العامة لتتناسب مع المعلمة النوع بالضبط. 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;