2012-01-17 16 views
11

Si he creado la siguiente Empleado objeto (simplificado) ...¿Puede un objeto personalizado C# contener una propiedad del mismo tipo que él?

public class Employee 
    { 
     public Employee() 
     {  
     } 

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

... ¿sería aceptable tener otra propiedad en el Empleado objeto con un tipo también ser Empleado para mantener sus detalles de administrador (como se muestra a continuación)?

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; } 
    } 

Además, ¿cuál es la mejor manera de crear una instancia del Empleado objeto para la propiedadManager? Obviamente, incluir this.Manager = new Employee(); en el constructor causará un ciclo infinito. ¿Sería el mejor Manager clase que hereda de Employee (aunque todas las propiedades serían idénticas)?

+3

@FelixK. Eso es un poco duro, es una buena pregunta y le interesó leer esto * ... en el constructor causará un bucle infinito *. Recuerde que todos no son expertos – V4Vendetta

+0

@ V4Vendetta Es verdad, no todos somos expertos. Pero cuando tengo un problema o pregunta voy a probar algunas formas de resolver la tarea que causa el problema. –

+0

@FelixK. Sí, lo probé y lo compilé bien. Esta es la razón por la cual mi pregunta era si era 'aceptable' en lugar de 'funcionará'. Quería comprobar si esto era una buena práctica o no más que nada. Da la casualidad que las soluciones a continuación con respecto a la ejemplificación del objeto son más o menos lo que había concluido también, pero una vez más, al ser relativamente inexperto, quería asegurarme de estar en buen camino. – triplestones

Respuesta

17

Un objeto puede de hecho tiene una referencia a un objeto de su propio tipo.

Así es como se implementan la mayoría de los objetos de tipo Node.

En cuanto a la creación de instancias, puede pasar el objeto Employee para usar como administrador (pasando nulo para ningún administrador). Los constructores pueden tener varias sobrecargas:

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

+1, buena comparación con los tipos 'Node'. – ken2k

+0

... pero obviamente no debe inicializar incondicionalmente la propiedad/campo en su constructor (ya que eso causaría una 'StackOverflowException' o una' OutOfMemoryException', dependiendo del tamaño del objeto, el tamaño de la pila asignada y su general memoria disponible). – Nuffin

+1

@Tobias El código anterior no va a causar ninguna excepción.Causaría uno si lo inicializa con 'this.Manager = new Employee()'. –

7

Sí, un objeto puede contener referencias a otros objetos de la misma clase.

Y en segundo lugar, no me gustaría crear un nuevo empleado en el cunstructor pero inyectarlo como esto:

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 para inyección de constructor! –

1

Funciona, sólo puede tratar s.th. como:

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

Sí, se puede tener en el interior EmployeeEmployee y que no causará bucle infinito, por defecto Manager propiedad de Employee objeto será null.

1

Específicamente en el tema de la construcción (he respondido +1 en respuesta de Odeds): como dices, construir una instancia en el constructor es un mal movimiento.

Pero luego pregúntate: ¿por qué necesitarías hacerlo de todos modos? En su caso Manager/Employee, no siempre se puede estar seguro de que un empleado siempre tenga un administrador, y si no lo hace, entonces no se debe usar una instancia vacía ed new para significar eso, sino un nulo.

Cuando su tipo tenga accesadores get/set públicos en las propiedades, generalmente es probable que cargue estos árboles de objetos de alguna fuente externa, en cuyo caso no tiene de qué preocuparse. Igualmente, puede tener un constructor que acepte otras Employee casos de relaciones Gerente/Empleado etc.

También debe comprobar para las relaciones circulares en ese constructor, así - es decir,un empleado no puede ser el gerente de alguien y su empleado; intente llevar la relación niño-padre para eso y vea si alguna vez termina.

0

En primer lugar, la respuesta es un objeto puede tener un campo que contenga una instancia de sí mismo. Incluso puede tener métodos que aceptan o devolver las instancias de la misma clase, e incluso puede depender de sí mismo en la definición de la clase, por ejemplo:

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; 
    } 
} 

Por defecto, todos los valores de referencia son null 'a fin d que no tendrá un problema de constructor a menos que crees uno tú mismo. Por lo tanto, , puede haber algunos problemas con las referencias circulares y los bucles sin fin (cada padre tiene hijos que tienen hijos que tienen padres, etc.) pero generalmente se pueden detectar y evitar de manera trivial.

Las únicas veces que he encontrado este tipo de problemas es cuando utilicé la serialización XML (u otra basada en texto) en objetos referenciados circularmente.

3

El único escenario donde este no es posible es con un struct; un struct está contenido directamente (en lugar de ser una referencia de tamaño fijo a los datos), por lo que el tamaño de una estructura Employee debería ser "el tamaño de los otros campos más el tamaño de un empleado", que es circular .

En particular, no se puede tener:

struct Foo { 
    Foo foo; 
} 

(o cualquier otra cosa que resultaría en una circular tamaño) - el compilador responde con:

miembro de

Struct 'Foo.foo' de tipo 'Foo' provoca un ciclo en el diseño struct

Sin embargo, en todos los otros casos está bien; con el tema de la inicialización, diría: simplemente déjalo sin asignar inicialmente, y deja que la persona que llama asigne un valor a través de la propiedad.

0

yo probamos este camino y que trabajó para mí:

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; 
    } 
} 

Ahora se puede utilizar en la función main() como:

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); 
    } 
} 
Cuestiones relacionadas