UserControl: пользовательский элемент управления, заканчивающийся в .ascx, который состоит из других веб-элементов управления. Почти как небольшая версия aspx веб-страницы. Это состоит из UI (ascx) и codebehind. Не может быть снова использован в других проектах путем ссылки на DLL.
WebControl: управление размещается на веб-странице или в UserControl. Это состоит из одного или нескольких классов, работающих в тандеме, и размещается на aspx странице или в UserControl. WebControls не имеют UI "страницей" и должны представить их содержание непосредственно. Они могут быть снова использованы в других приложениях путем ссылки на их DLLs.
RenderedControl: не существует. Может быть синонимично с WebControl. Мог бы указать, что управление записано непосредственно в HttpResponse, а не представлено к aspx странице.
CompositeControl: Промежуток UserControls и WebControls. Они кодируют как UserControls, поскольку они состоят из других средств управления. Нет никакого графического UI для составления композита управления, и поддержка редактирования UI CompositeControls должна быть кодирована разработчиком управления. Составление композита сделано в codebehind. CompositeControls может быть снова использован в других проектах как WebControls.
Вы решаете не ту проблему. Если у вас есть суперкласс A
с подклассами B
, C
и т. Д., Которые имеют схожую функциональность, вы хотите сделать следующее:
Сделайте A
интерфейсом, который реализует B
, C
и т. Д. Код, который работает с экземплярами B
или C
, работает через интерфейс, предоставляемый A
. Если вы можете определить общий набор операций, которые работают со всеми типами, то это все, что вам нужно сделать.
Если вы не можете определить общий набор операций, например, у вас есть код, похожий на:
А foo = GetA ();
if (foo is B) {
B bFoo = (B) foo;
// Делаем что-нибудь с foo как B
} else if (foo is C) {
C cFoo = (C) foo;
// Делаем что-нибудь с foo как C
} ...
Или даже это (что в основном то же самое, просто используется дополнительная информация для имитации того, что система типов уже предоставляет вам):
A foo = GetA ();
MyEnum enumeratedValue = foo.GetEnumeratedValue ();
switch (enumeratedValue) {
case MyEnum.B:
B bFoo = (B) foo;
// Делаем что-нибудь с foo как B
сломать;
case MyEnum.C:
C cFoo = (C) foo;
// Делаем что-нибудь с foo как C
сломать;
}
Тогда вам действительно нужно сделать что-то вроде:
A foo = GetA ();
foo.DoSomething ();
Где каждый подкласс будет реализовывать соответствующую ветвь оператора switch
. На самом деле это лучше в нескольких отношениях:
переключатель
/ case
блок отдельно от фактического B
и C
, вы не рискуете случайно забыть добавить соответствующий case
при добавлении нового подкласса. Если вы оставите метод DoSomething ()
вне подкласса A
, вы получите ошибку времени компиляции. Изменить : В ответ на ваш комментарий:
Если ваша подпрограмма DoSomething ()
должна работать с формой
или другим элементом графического интерфейса пользователя, просто передайте этот элемент в метод. Например:
public class B : A {
public void DoSomething(MyForm form) {
form.MyLabel.Text = "I'm a B object!";
}
}
public class C : A {
public void DoSomething(MyForm form) {
form.MyLabel.Text = "I'm a C object!";
}
}
// elsewhere, in a method of MyForm:
A foo = GetA();
foo.DoSomething(this);
В качестве альтернативы, еще лучше было бы превратить ваши классы B
и C
в настраиваемые элементы управления, поскольку они, похоже, инкапсулируют логику отображения.
Вы можете создать общий метод:
public T GetAppointment<T>(int id) where T : tblAppointment
{
var singleAppointment = dc.tblAppointments.SingleOrDefault(a => a.appID == id);
return (T)singleAppointment;
}
Но тогда вам нужно будет знать фактический тип объекта перед его вызовом ...
Ну, если вы используете C # 4, вы можете использовать динамическую типизацию ... но если вы хотите придерживаться статической типизации, я думаю, лучшее, что вы можете сделать предоставляет ожидаемый тип в качестве аргумента универсального типа и получает метод для выполнения преобразования для вас:
public T GetAppointment<T>(int id) where T : tblAppointment
{
var singleAppointment = (from a in dc.tblAppointments
where a.appID == id
select a).SingleOrDefault();
return (T) singleAppointment;
}
Вызовите это с помощью:
SpecificAppointment app = GetAppointment<SpecificAppointment>(10);
или используйте неявную типизацию:
var app = GetAppointment<SpecificAppointment>(10);
Это вызовет исключение во время выполнения, если приведение не выполняется.
Предполагается, что вызывающий абонент знает тип встречи (хотя они могут указать tblAppointment
, если не знают). Не зная соответствующий тип встречи во время компиляции, трудно понять, как статическая типизация может оказать вам больше пользы, правда ...
Когда вы вызываете .GetType ()
, вы получаете тип среды выполнения объекта. Компилятор C # не знает, какой тип среды выполнения будет у вашего объекта. Он знает только, что ваш объект будет иметь тип, производный от tblAppointment
, потому что вы сказали это в объявлении вашего метода, поэтому статический тип возвращаемого значения - tblAppointment
. Поэтому tblAppointment
- это все, к чему вы можете получить доступ, если вы не используете приведение, чтобы сообщить компилятору: «Я знаю, что во время выполнения эта ссылка будет ссылаться на объект этого типа, вставьте проверку времени выполнения и дайте мне ссылка на этот статический тип ».
Статическая типизация - это все о различии между типами, известными во время компиляции и во время выполнения. Если вы пришли с языка с динамической типизацией, такого как Smalltalk или Javascript, вам придется внести немало изменений в свои привычки программирования и мыслительные процессы. Например, если вам нужно сделать что-то с объектом, который зависит от его типа среды выполнения, решение часто состоит в использовании виртуальных функций - они отправляются на тип среды выполнения объекта.
Обновление: в вашем конкретном случае используйте виртуальные функции, это именно то, для чего они были созданы:
class tblAppointment
{
protected abstract void ProcessAppointment () ;
}
sealed class tblBirthAppointment
{
protected override void ProcessAppointment ()
{
// `this` is guaranteed to be tblBirthAppointment
// do whatever you need
}
}
...
Затем используйте
// will dispatch on runtime type
appointmentsRepo.GetAppointment (id).ProcessAppointment () ;
Вы можете создать другой метод для инкапсуляции приведения:
public tblSingleBirthAppointment GetBirthAppointment(int id)
{
var singleAppointment = GetAppointment(id);
if (singleAppointment != null)
{
return (tblSingleBirthAppointment)singleAppointment;
}
return null;
}
Этот метод сломается, если вы попытаетесь использовать его с идентификатором, который на самом деле не был назначением рождения, поэтому вы можете рассмотреть возможность проверки .
var viewSingleBirthAppointment = appointmentRepos.GetBirthAppointment(appointmentId);
Если вы возвращаете ссылку на дочерний тип, который является родительским типом, ссылка будет относиться к этому типу, и компилятор не позволит вам получить доступ ни к одному из членов дочернего типа, пока вы не приведете к этому типу. Это полиморфизм в действии :)
Хорошая новость в том, что вы не создаете новый объект, когда приводите ссылочный тип - вы просто меняете тип ссылки, которая указывает на уже имеющийся у вас объект, тем самым предоставляя вам доступ своим членам.