2012-01-17 24 views
11

如果我已经创建了如下雇员对象(简化的)...自定义C#对象是否可以包含与其自身相同类型的属性?

public class Employee 
    { 
     public Employee() 
     {  
     } 

     public String StaffID { get; set; } 
     public String Forename { get; set; } 
     public String Surname { get; set; } 
    } 

...这将是可以接受的在雇员对象的另一属性与类型也正在雇员保留经理的细节(如下所示)?

public class Employee 
    { 
     public Employee() 
     {  
     } 

     public String StaffID { get; set; } 
     public String Forename { get; set; } 
     public String Surname { get; set; } 

     public Employee Manager { get; set; } 
    } 

此外,什么是实例为经理财产员工对象的最佳方式?显然在构造函数中包含this.Manager = new Employee();将导致无限循环。是经理继承雇员是最好的方式(即使所有的属性将是相同的)?

+3

@FelixK:

class Program { static void Main(string[] args) { A a = new A(new A()); } } public class A { public string Name { get; set; } public A a; public A() { } public A(A _a) { a = _a; } } 

现在你可以在main()函数一样使用它。这有点苛刻,它的一个很好的问题,并采取了一些兴趣阅读这个* ..在构造函数将导致无限循环*。记住所有不是专家 – V4Vendetta 2012-01-17 10:18:09

+0

@ V4Vendetta这是事实,我们并不都是专家。但是,当我遇到问题或问题时,我会测试一些解决导致问题的任务的方法。 – 2012-01-17 10:33:35

+0

@FelixK。是的,我确实尝试过,它编译得很好 - 这就是为什么我的问题是它是“可接受的”而不是“它会工作” - 我想检查这是否是好的做法或不是什么。碰巧,下面关于对象实例化的解决方案几乎与我所得出的结论一样,但又相对缺乏经验,我想确保我正常运行。 – triplestones 2012-01-17 11:01:25

回答

17

一个对象可以确实有一个对自己类型的对象的引用。

这是大多数Node类型对象的实现方式。

至于实例化 - 您可以传入Employee对象以用作管理器(传递null没有管理器)。构造器可以有多个过载:

public Employee(Employee manager) 
{ 
    this.Manager = manager; 
} 
+1

+1,与'Node'类型进行比较。 – ken2k 2012-01-17 10:06:19

+0

...但显然你不应该无条件地初始化你的构造函数中的属性/字段(因为这会导致StackOverflowException或OutOfMemoryException,这取决于对象的大小,分配的堆栈大小和整体有效内存)。 – Nuffin 2012-01-17 10:09:03

+1

@Tobias上面的代码不会导致任何异常。如果你用'this.Manager = new Employee()'初始化它会导致一个。 – 2012-01-17 10:12:23

7

是的,一个对象可以包含对同一类的其他对象的引用。

其次,我不会创建在cunstructor一个新员工,但注入这样的:

public class Employee 
{ 
    public Employee(Employee manager) 
    { 
     this.Manager = manager; 
    } 

    public String StaffID { get; set; } 
    public String Forename { get; set; } 
    public String Surname { get; set; } 

    public Employee Manager { get; set; } 
} 
+0

+1构造注入! – 2012-01-17 10:08:08

1

它的工作原理,你可以尝试某事物像:

public class A 
{ 
    public A test { get; set; } 
} 
2

是的,你可以有EmployeeEmployee并不会造成死循环,在默认情况下Employee对象Manager属性将是null

1

特别是关于构建问题(我已经+1了Odeds答案) - 正如你所说在构造函数中构造一个实例是一个坏的举动。

但后来问自己 - 为什么你需要反正。在你的Manager/Employee案例中 - 你不能总是确定一个雇员总是有一个经理,如果他们不这样做,那么你不应该使用一个空实例来表示这个,而是一个空。

当你的类型在属性上有公共get/set访问器时,通常你可能会从某些外部源加载这些对象树,在这种情况下你没有什么可担心的。同样,您可以拥有一个构造函数,以接受其他Employee实例的经理/员工关系等。

您还应该检查该构造函数中的循环关系 - 即一个员工不能成为某人的经理和他们的员工 - 试着为孩子走亲子关系,看看它是否结束!

0

首先,答案是是的一个对象可以有一个字段包含自己的一个实例。它甚至可以接受的方法或返回相同的类的实例,它甚至可以依靠自身在类的定义,如:

public class Person : IComparable<Person> //legal, recursive definition 
{ 
    //fields (or properties) that are of type Person 
    public Person Father; 
    public Person Mother; 
    public List<Person> Children; 

    // method that takes a Person as a parameter 
    public bool IsParent(Person potentialParent) 
    { 
     .... 
    } 

    //method that returs a Person 
    public Person Clone() 
    { 
     //TODO: real implementation coming soon 
    } 

    public Person(){} 

    //constructor that takes persons as arguments 
    public Person(Person father, Person Mother) 
    { 
     Father = father; 
     Mother = mother; 
    } 
} 

默认情况下,所有的参考值是null“D所以你除非你自己创建一个构造函数,否则不会有构造函数问题。所以,是的,可以有一些循环引用和无限循环的问题(每个父母有孩子,有孩子,有父母等),但通常他们可以平凡地检测和避免。

我遇到这类问题的唯一时间是当我在循环引用的对象上使用XML(或其他基于文本的)序列化时。

3

不是可能的唯一情况是struct;一个struct包含直接(而不是一个固定大小的参考数据),所以Employee结构的大小将必须是“其他领域的大小加上雇员的大小”,这是圆形。

特别是你不能有: - :

结构成员 'Foo.foo'

struct Foo { 
    Foo foo; 
} 

(或其他任何将导致圆形大小)编译器响应'Foo'类型会导致结构布局中的循环

但是,在所有其他情况下,它都是好的;在初始化的问题上,我会说:只是最初不分配它,让调用者通过属性分配一个值。

0

我试过这种方式,它的工作对我来说:

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(new A()); 
     a.Name = "Roger"; 
     a.a.Name = "John"; 
     Console.WriteLine("{0}, {1}", a.Name, a.a.Name); 
    } 
} 
相关问题