У вас есть таблица для хранения основных данных свойства (в этой таблице вы добавляете к ней дополнительные поля по мере необходимости, пока я не получу окончательный дизайн таблицы) Пример: main_property_file (property_id PK, address, GPS_location) Затем вам нужно знать, что это это для аренды или продажи, справочная таблица сборки содержит это, например: property_for (typefor PK, назовите это). И собираюсь добавить файл в предыдущем файле main_property_file для typefor, поданный как внешний ключ. И так далее, пока вы не поместите всю структуру бизнес-данных, затем чтобы проверить таблицы и отношения на предмет согласованности, вы можете поместить это в визуальный инструмент, если не можете представить связь и данные, которые хранятся в каждой таблице, и, наконец, попросить вашу модель с запросом найти информацию или ввести новые данные и так далее, чтобы убедиться, что Разработка таблицы завершена, и пересмотрите ограничения .
Я вполне уверен, Вы не повезло до используете компилятор и дженерики для сохранения Вас некоторые проверки на этапе выполнения. Вы не можете переопределить что-то, что уже не существует, и у Вас не может быть различных типов возврата к тем же методам.
Я не могу сказать, что полностью понимаю Вашу мотивацию, но она имеет техническую заслугу.
Моя первая попытка использовала базовый класс, имеющий Невиртуальный открытый интерфейс и затем защищающий другого виртуальный метод CheckCreatedType
это позволило бы чему-либо в цепочке осматривать тип, прежде чем базовый класс Создаст, был назван.
public class A
{
public IFieldSimpleItem Create()
{
IFieldSimpleItem created = InternalCreate();
CheckCreatedType(created);
return created;
}
protected virtual IFieldSimpleItem InternalCreate()
{
return new SimpleImpl();
}
protected virtual void CheckCreatedType(IFieldSimpleItem item)
{
// base class doesn't care. compiler guarantees IFieldSimpleItem
}
}
public class B : A
{
protected override IFieldSimpleItem InternalCreate()
{
// does not call base class.
return new NormalImpl();
}
protected override void CheckCreatedType(IFieldSimpleItem item)
{
base.CheckCreatedType(item);
if (!(item is IFieldNormalItem))
throw new Exception("I need a normal item.");
}
}
Следующее всовывает проверку времени выполнения в базовом классе. Неразрешимая проблема - Вы, все еще должны полагаться на называемый метод базового класса. Неправильно себя ведущий подкласс может повредиться, все проверяет не вызов base.CheckCreatedType(item)
.
Альтернативы - Вы hardcode все проверки на все подклассы в базовом классе (плохо) или иначе воплощают проверку.
Попытка 2: Классы (Sub) регистрируют проверки, в которых они нуждаются.
public class A
{
public IFieldSimpleItem Create()
{
IFieldSimpleItem created = InternalCreate();
CheckCreatedType(created);
return created;
}
protected virtual IFieldSimpleItem InternalCreate()
{
return new SimpleImpl();
}
private void CheckCreatedType(IFieldSimpleItem item)
{
Type inspect = this.GetType();
bool keepgoing = true;
while (keepgoing)
{
string name = inspect.FullName;
if (CheckDelegateMethods.ContainsKey(name))
{
var checkDelegate = CheckDelegateMethods[name];
if (!checkDelegate(item))
throw new Exception("failed check");
}
if (inspect == typeof(A))
{
keepgoing = false;
}
else
{
inspect = inspect.BaseType;
}
}
}
private static Dictionary<string,Func<IFieldSimpleItem,bool>> CheckDelegateMethods = new Dictionary<string,Func<IFieldSimpleItem,bool>>();
protected static void RegisterCheckOnType(string name, Func<IFieldSimpleItem,bool> checkMethod )
{
CheckDelegateMethods.Add(name, checkMethod);
}
}
public class B : A
{
static B()
{
RegisterCheckOnType(typeof(B).FullName, o => o is IFieldNormalItem);
}
protected override IFieldSimpleItem InternalCreate()
{
// does not call base class.
return new NormalImpl();
}
}
Проверка сделана подклассом, регистрирующим делегата для вызова в базовом классе, но без базового класса, зная все правила заранее. Заметьте также, что это - все еще Невиртуальный открытый интерфейс, который позволяет базовому классу проверять результаты прежде, чем возвратить их.
Я предполагаю, что это - ошибка разработчика, которую Вы пытаетесь зафиксировать. Если это применимо, можно украсить метод проверки на этапе выполнения System.Diagnostics.Conditional("DEBUG")]
, разрешение Версии выпуска пропустить проверки.
Мое знание дженериков не прекрасно, поэтому возможно, это является ненужным. Однако проверки здесь не должны быть для одного только типа: это могло быть адаптировано к другому использованию. например, делегат передал в Register..
не должен просто проверять, что ссылка является определенным типом'
* Примечание, что, вероятно, не хорошо создать словарь по имени типа, как записано выше; эта работа немного упрощенна для иллюстрирования используемого механизма.
Это не позволяется, потому что это нарушает принцип замены Лисков.
Скажем, у Вас есть другой интерфейс:
public interface IFieldSuperItem : IFieldSimpleItem
Вы затем могли бы сделать это
Person p = new Boss();
p.Create<IFieldSuperItem>();
Вызов во второй строке, в то время как совместимый с определением Создают лично, но очевидно не совместимые с определенным в Боссе (которые только работают с IFieldNormalItem и его подклассом).
Я думаю, что проблема состоит в том, что Вы переопределяете ранее определенный метод. Так эффективно Вы пытаетесь изменить определение метода, который не разрешен. Ваш единственный выбор состоит в том, чтобы или создать новый метод, например.
public class Bose : Person
{
public virtual T CreateNormal<T>()
where T : IFieldNormalItem //This is where the error is
{
return default(T);
}
}
или потребуйте нормального поля на классе Человека или сделайте проверку динамично.
Изменение универсального ограничения изменяет сигнатуру метода, которая не позволяется, если Вы переопределяете виртуальное.
Я думаю, что Вы, возможно, должны разделить Создать метод на отдельный класс:
public interface IFieldSimpleItem { }
public interface IFieldNormalItem : IFieldSimpleItem{ }
public interface IFieldCreator<TField, TPerson> where TField : IFieldSimpleItem where TPerson : Person
{
TField Create(TPerson person);
}
public class Person
{
}
public class Bose : Person
{
}
public class PersonFieldCreator : IFieldCreator<IFieldSimpleItem, Person>
{
public IFieldSimpleItem Create(Person person) { return null; }
}
public class BoseFieldCreator : IFieldCreator<IFieldNormalItem, Bose>
{
public IFieldNormalItem Create(Bose person) { return null; }
}
Самый простой пример - то, что это повреждает полиморфизм. Если бы у Вас был набор Человека, где один или несколько из тех объектов имеет тип Bose, это отказало бы, как только это поражает Bose.
Person[] people;
[...initialize this somewhere...]
foreach(Person p in people)
p.Create<IFieldSimpleItem>();
Кажется, что Вы не можете изменить определение метода, но можно ли сделать классы универсальными вместо Создать Метода?
public class Person<T> where T : IFieldSimpleItem
{
public virtual T Create()
{
return default(T);
}
}
public class Bose<T> : Person<T> where T : IFieldNormalItem
{
public override T Create()
{
return default(T);
}
}
Что относительно этого:
public interface IFieldNormalItem : IFieldSimpleItem
{ }
public class Person<T> where T : IFieldSimpleItem
{
public virtual T Create()
{
return default(T);
}
}
Теперь Вы можете иметь Person<IFieldSimpleItem>
(соответствует Person
) или Person<IFieldNormalItem>
(соответствует Bose
).
Кода ниже достаточно для переопределения. Тип T уже обозначается как потребность, которая будет реализована IFieldSimpleItem в Человеке базового класса.
public class Bose : Person
{
public override T Create<T>()
// where T : IFieldNormalItem // You don't need this line.
{
return default(T);
}
}
РЕДАКТИРОВАНИЕ: Я полностью понял вопрос превратно, таким образом, код выше не решит этот случай. Единственная вещь, которую необходимо сделать; не переопределять Создать метод "переопределением", но "виртуальный".
public class Bose : Person
{
public virtual T Create<T>()
where T : IFieldNormalItem
{
return default(T);
}
}