Яка мета цього очевидного посилання на C #?


21

Я оцінюю CMS з відкритим кодом під назвою Piranha ( http://piranhacms.org/ ) для використання в одному з моїх проектів. Наступний код мені здався цікавим і трохи заплутаним, принаймні для мене. Чи може хтось допомогти мені зрозуміти, чому клас успадковується від бази одного типу?

public abstract class BasePage<T> : Page<T> where T : BasePage<T>
{
    /// <summary>
    /// Gets/sets the page heading.
    /// </summary>
    [Region(SortOrder = 0)]
    public Regions.PageHeading Heading { get; set; }
}

Якщо визначається клас BasePage<T>, чому слід успадковувати Page<T> where T: BasePage<T>? Якому конкретному призначенню воно служить?



7
Незважаючи на голосування та близькі голоси, я думаю, що громада помилилася на цьому. Це чітко поставлене питання, пов'язане з конкретним і нетривіальним дизайнерським рішенням, сутністю того, про що йдеться в цьому веб-сайті.
Роберт Харві

Просто повісьте і знову відкрийте його, коли він закриється.
Девід Арно

Прочитайте про концепцію F-обмеженого поліморфізму :)
Eyvind

1
@Eyvind я насправді так і зробив. Для тих, хто цікавиться читати про F-Bounded поліморфізм, ось посилання staff.ustc.edu.cn/~xyfeng/teaching/FOPL/lectureNotes/…
Xami Yen

Відповіді:


13

Чи може хтось допомогти мені зрозуміти, чому клас успадковується від бази одного типу?

Це не так, це успадковується від Page<T>, але Tсаме воно обмежується параметризуватися типом, від якого походить BasePage<T>.

Щоб зробити висновок чому, ви повинні подивитися, як Tнасправді використовується параметр типу . Після деякого копання, просуваючись по ланцюжку спадкування, ви перейдете до цього класу:

( github )

public class GenericPage<T> : PageBase where T : GenericPage<T>
{
    public bool IsStartPage {
        get { return !ParentId.HasValue && SortOrder == 0; }
    }

    public GenericPage() : base() { }

    public static T Create(IApi api, string typeId = null)
    {
        return api.Pages.Create<T>(typeId);
    }
}

Наскільки я бачу, єдиною метою загального обмеження є переконання, що Createметод повертає найменш можливий абстрактний тип.

Не впевнений, чи варто цього, але, можливо, в цьому є якась вагома причина, або це може бути лише для зручності, або, можливо, за ним немає занадто багато речовини, і це просто надмірно розроблений спосіб уникнути виступу (BTW, я Я не маю на увазі, що це саме так, я просто кажу, що люди іноді роблять це).

Зверніть увагу , що це не дозволяє їм уникнути відображення - api.Pagesє сховищем сторінок, які отримують typeof(T).Name, і передає його як typeIdдо contentService.Createметоду ( дивіться тут ).


5

Одне поширене використання цього пов'язане з поняттям власних типів: параметр типу, який відповідає поточному типу. Скажімо, ви хочете визначити інтерфейс із clone()методом. clone()Метод завжди повинен повертати екземпляр класу , на якому він був викликаний. Як ви заявляєте про цей метод? У системі генерики, яка має власні типи, це легко. Ви просто говорите, що це повертається self. Отже, якщо у мене є клас Foo, метод повернення повинен бути оголошений для повернення Foo. У Java та (з побіжного пошуку) C # це не варіант. Натомість ви бачите декларації, як те, що ви бачите в цьому класі. Важливо розуміти, що це не те саме, що для самовведення, і обмеження, які він надає, слабші. Якщо у вас є клас Fooі Barклас, з якого походять обидваBasePage, ви можете (якщо я не помиляюся) визначити Foo для параметра Bar. Це може бути корисним, але я думаю, як правило, більшу частину часу це буде використовуватися як власний тип, і це просто зрозуміло, що хоч ти можеш обходити і замінювати інші типи, це не те, що ти повинен робити. Я давно розігрувався з цією ідеєю, але дійшов висновку, що докладати зусиль не варто, оскільки обмеження Java-дженерики. Звичайно, загальна інформація про C # є більш повною, але, мабуть, має таке саме обмеження.

Інший раз, коли цей підхід застосовується, це коли ви будуєте такі типи графіків, як дерева або інші рекурсивні структури. Декларація дозволяє типам відповідати вимогам сторінки, але додатково уточнювати тип. Ви можете бачити це в структурі дерева. Наприклад, Nodeможе бути параметризований параметр, Nodeякий дозволить реалізаціям визначити, що це не просто Дерева, що містять будь-який тип Вузла, а певний підтип Вузла (як правило, їх власний тип.) Я думаю, що це більше того, що тут відбувається.


3

Будучи людиною, яка насправді написала код, я можу підтвердити, що Філіп правильний, а самонавідний загальний фактично є зручністю для надання типового методу Create у базовому класі.

Як він згадує, все ще відбувається багато роздумів, і врешті-решт для вирішення типу сторінки використовується лише назва типу. Причиною цього є те, що ви також можете завантажувати динамічні моделі, тобто матеріалізувати модель, не маючи доступу до типу CLR, який створив її в першу чергу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.