У меня есть три класса, которые определены следующим образом:
public class Base
{
public int Id { get; set; }
}
public class Bar : Base { ... }
public class Foo : Base
{
public int? ParentId { get; set; }
public Base Parent { get; set; }
public int? FooParentId { get; set; }
public Foo FooParent { get; set; }
public int? BarParentId { get; set; }
public Bar BarParent { get; set; }
public Foo(Foo parent)
{
FooParent = parent;
FooParentId = parent.Id;
Parent = FooParent; // Now changes to Parent are reflected on FooParent
ParentId = FooParentId; // Changes to ParentId are not reflected because it is a value type.
}
public Foo(Bar parent)
{
// Same implementation as ctor(Foo parent);
}
}
То, что я пытаюсь сделать, это Parent
ссылка либо FooParent
или BarParent
(который я считаю, это довольно просто,), а затем ParentId
ссылочный FooParentId
или BarParentId
. Каков самый разумный способ сделать ParentId
ссылочным типом?
В ответе здесь упоминается, что бокс/распаковка целого числа имеет множество проблем, связанных с ним.
Это действительно плохое решение?
Редактировать:
Я должен был упомянуть, что Foo
и Bar
являются таблицами Entity Framework. Foo
- это объект, который может быть дочерним для одной или другой таблицы, а для кода Entity Framework - сначала правильно генерировать таблицу, необходимо определить как FooParent
и BarParent
. Кроме того, ParentId
может не ссылаться на Parent
в его получателе. Entity Framework тоже виноват в этом. Entity Framework Reinserts Существующие объекты при вызове context.Set<Foo>().Add(newItem);
, Все родители, которые не являются нулевыми, повторно вставлены.
Если ваши руки связаны с EF, вы можете использовать делегат для распространения изменения ParentId
на основе типа, который был передан в конструкторе. Я не знаю, будет ли EF жаловаться на это или нет.
Например, используя делегат Action<T>
, мы можем перенаправить это изменение в ParentId
на соответствующий [Foo|Bar]ParentId
:
public class Base
{
public int Id { get; set; }
}
public class Bar : Base { }
public class Foo : Base
{
private int? _parentId;
public int? ParentId {
get{ return _parentId; }
set{
_parentId = value;
if( OnParentChangeAction != null ) {
OnParentChangeAction( _parentId );
}
}
}
public Base Parent { get; set; }
public int? FooParentId { get; set; }
public Foo FooParent { get; set; }
public int? BarParentId { get; set; }
public Bar BarParent { get; set; }
private Action<int?> OnParentChangeAction{ get; set; }
public Foo(Foo parent)
{
FooParent = parent;
FooParentId = parent.Id;
Parent = FooParent;
ParentId = FooParentId;
OnParentChangeAction = newParentId => FooParentId = newParentId;
}
public Foo(Bar parent)
{
BarParent = parent;
BarParentId = parent.Id;
Parent = BarParent;
ParentId = BarParentId;
OnParentChangeAction = newParentId => BarParentId = newParentId;
}
}
Вы можете добавить логику для get
и set
аксессуаров ваших FooParentId
и BarParentId
, но согласитесь с @Servy, что это плохой дизайн.
И причина состоит не только в том, что у них есть как ID, так и ссылки на объекты. Я не понимаю, зачем вам нужно иметь BarParent и FooParent, просто иметь 1 свойство типа IParent
а затем обмениваться данными с ним, используя методы, которые предоставляет этот интерфейс, используя все преимущества наследования и полиморфизма.
Поскольку у вас уже есть ссылка на родительский объект, вы можете получить свой идентификатор с свойством:
public int? ParentId
{
get
{
return Parent != null ? Parent.Id : null;
}
}
event
C # для эмуляции концептуальных событий, чем делегат в качестве свойства.event
если вы не используете ни одну из функций, предоставляемых событиями, например многоадресную рассылку? Если бы это была публично выставленная собственность, я бы согласился, но это частный участник, иevent
излишне.