Я работаю с .NET framework, и я действительно хочу иметь возможность создавать страницы нестандартного типа, которые использует весь мой веб-сайт. Проблема возникает, когда я пытаюсь получить доступ к странице из элемента управления. Я хочу иметь возможность возвращать мой конкретный тип страницы вместо страницы по умолчанию. Есть какой-либо способ сделать это?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
c#
covariance
Кайл Слеттен
источник
источник
Ответы:
ОБНОВЛЕНИЕ: этот ответ был написан в 2011 году. После двух десятилетий того, как люди предлагали ковариантность возвращаемого типа для 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() { ... } }
Это безопасно, потому что потребители Контента через вольер ожидают животного, и Аквариум обещает не только выполнить это требование, но, более того, дать более строгое обещание: животное всегда будет рыбой.
Этот вид ковариации не поддерживается в C # и вряд ли когда-либо будет поддерживаться. Он не поддерживается средой CLR. (Он поддерживается C ++ и реализацией C ++ / CLI в CLR; он делает это путем создания волшебных вспомогательных методов того типа, который я предлагаю ниже.)
(Некоторые языки также поддерживают контравариантность формальных параметров - вы можете переопределить метод, принимающий Fish, методом, принимающим Animal. И снова контракт выполняется; базовый класс требует, чтобы любая 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(); }
источник
Я не пробовал, но разве это не работает?
источник
Да. Есть несколько способов сделать это, и это только один из них:
Вы можете заставить свою страницу реализовывать некоторый настраиваемый интерфейс, который предоставляет метод под названием «GetContext» или что-то в этом роде, и он возвращает вашу конкретную информацию. Затем ваш элемент управления может просто запросить страницу и выполнить приведение:
var myContextPage = this.Page as IMyContextGetter; if(myContextPage != null) var myContext = myContextPage.GetContext();
Затем вы можете использовать этот контекст, как хотите.
источник
Вы можете получить доступ к своей странице из любого элемента управления, поднявшись по родительскому дереву. То есть
myParent = this; while(myParent.parent != null) myParent = myParent.parent;
* Не компилировал и не тестировал.
Или получите родительскую страницу в текущем контексте (зависит от вашей версии).
Тогда мне нравится вот что: я создаю интерфейс с функциями, которые хочу использовать в элементе управления (например, IHostingPage).
Затем я приводил родительскую страницу «IHostingPage host = (IHostingPage) Parent;» и я готов вызвать функцию на нужной мне странице под своим контролем.
источник
Я сделаю это так:
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; } } }
источник