什么';在C#中,将值类型更改为引用类型的最佳方法是什么?

什么';在C#中,将值类型更改为引用类型的最佳方法是什么?,c#,entity-framework,reference,C#,Entity Framework,Reference,我有三类定义如下: 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; } pub

我有三类定义如下:

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
是实体框架表
Foo
是一个对象,它可能是一个表或另一个表的子对象,要使实体框架代码首先正确生成表,必须同时定义
FooParent
BarParent
。另外,
ParentId
在其getter中可能不会引用
Parent
。实体框架也应该为此负责。调用
context.Set().Add(newItem)时的实体框架。将重新插入所有非空的父对象。

因为您已经有了对父对象的引用,所以可以使用以下属性获取其Id:

    public int? ParentId 
    { 
        get
        {
            return Parent != null ? Parent.Id : null;
        }
    }

您可以向
FooParentId
BarParentId
属性的
get
set
访问器添加逻辑,但同意@Servy的观点,即这是一个糟糕的设计


原因甚至不仅仅是ID-s和对对象的引用。我不明白为什么您需要同时拥有BarParent和FooParent,只需要拥有一个类型为
IParent
的属性,然后使用此接口提供的方法与之通信,利用继承和多态性的所有好处。

如果您的手被EF束缚,您可以使用委托根据构造函数中传递的类型传播父ID的更改。我不知道EF是否会对此抱怨

例如,使用
操作
委托,我们可以将对
父ID
的更改转发到相应的
[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;
    }
}
公共类基
{
公共int Id{get;set;}
}
公共类栏:基{}
公共类Foo:Base
{
private int?\u parentId;
公共int?父ID{
获取{return\u parentId;}
集合{
_parentId=值;
if(OnParentChangeAction!=null){
OnParentChangeAction(_parentId);
}
} 
}
公共基父项{get;set;}
public int?FooParentId{get;set;}
公共Foo foopparent{get;set;}
公共int?BarParentId{get;set;}
公共条BarParent{get;set;}
ParentChangeAction{get;set;}上的私有操作
公共Foo(Foo家长)
{
FooParent=父;
FooParentId=parent.Id;
父母=父母;
ParentId=FooParentId;
OnParentChangeAction=newParentId=>FooParentId=newParentId;
}
公共食品(酒吧家长)
{
BarParent=父母;
BarParentId=parent.Id;
家长=家长;
ParentId=BarParentId;
OnParentChangeAction=newParentId=>BarParentId=newParentId;
}
}

您需要花一些时间阅读值类型(不是primative,它们被称为值类型)和引用类型。将引用更改为指向新引用会更改该变量的值,并且不会影响引用旧引用的任何其他变量。你不是在改变引用,而是在改变变量以引用一个新实例。同时保留对象的整数ID和对象本身似乎也是一个糟糕的设计。如果需要,对象本身有自己的ID值,就像对象的ID一样,而不是保留副本。难道不能让属性委托给相应的属性吗<代码>公共int?ParentId{get{return Parent!=null?Parent.Id:(int?)null;}}
@Servy我同意,这是一个糟糕的设计。但是,我使用的是EntityFramework,需要修复EntityFramework中的另一个设计缺陷。如果您感兴趣,请解释。这是一个很好的建议,但不幸的是,它对我不起作用。实体框架要求父ID与父引用解耦。这里有更详细的解释,你在这里看到的是一个概念上的事件。您会发现最好使用C#
事件
来模拟概念事件,而不是将委托作为属性。如果您不使用事件提供的任何功能(如多播),为什么“最好”使用
事件
?如果这是一个公开的属性,我会同意,但这是一个私有成员,
事件
是一个滥杀。多播不是
事件
的属性,而是代理的属性。C#中的所有代理都是多播代理。您在那里使用的代理是多播代理。使用
事件
的主要原因之一是,代码在语义上与您的需求相似。当代码更有效地表达其含义时,其他人就更容易理解它。以多播方式使用标准委托的情况非常罕见。事件提供的发布者/订阅者语义在仅限于声明类型时没有意义。更不用说活动所需的所有仪式了。您需要创建一个
EventArgs
类来将数据传递给处理程序。您需要包括
发送方
,即使它都在声明类型中。我想我们必须同意不同意。不,你不需要做这两件事。更改的范围是将行的声明更改为:
private event Action ParentChanged完成。最好将事件订阅从
=