1219 votos

Miembro Virtual de llamar a un constructor

Estoy recibiendo una advertencia de ReSharper acerca de una llamada a un miembro virtual de mis objetos constructor. ¿Por qué habría de ser esto algo que no se que hacer?

1091voto

Greg Beech Puntos 55270

(Suponiendo que está escrito en C#)

Cuando un objeto escrito en C# se construye, lo que pasa es que los inicializadores se ejecutan en el orden de la mayoría de la clase derivada de la clase base y, a continuación, los constructores se ejecutan en el orden de la clase base para la mayoría de la clase derivada (ver Eric Lippert del blog para detalles de por qué esto es).

También en .NET no objetos de tipo de cambio como están construidos, pero al principio como la mayoría de los derivados de tipo, con el método de la tabla para la mayoría de los derivados de tipo. Esto significa que el método virtual llamadas siempre se ejecuta en la mayoría de los derivados de tipo.

Cuando se combinan estos dos hechos que están a la izquierda con el problema de que si haces un método virtual llamada en un constructor, y no es la mayoría de los derivados de tipo, en su jerarquía de herencia, que será llamado en una clase cuyo constructor no ha sido ejecutado, y por lo tanto no puede estar en un adecuado estado de tener que método llamado.

Este problema es, por supuesto, mitigado si usted marca su clase como sellados para asegurarse de que es la mayoría de los derivados de tipo en la jerarquía de herencia, en cuyo caso es perfectamente seguro para llamar al método virtual.

554voto

Matt Howells Puntos 20751

Con el fin de responder a su pregunta, considere esta pregunta: ¿cuál será el siguiente código de impresión cuando la Child objeto es instanciado?

class Parent
{
    public Parent()
    {
        DoSomething();
    }
    protected virtual void DoSomething() {};
}

class Child : Parent
{
    private string foo;
    public Child() { foo = "HELLO"; }
    protected override void DoSomething()
    {
        Console.WriteLine(foo.ToLower());
    }
}

La respuesta es que, de hecho, un NullReferenceException serán arrojados, porque foo es null. Un objeto de la base de constructor es llamado antes que su propio constructor. Por tener un virtual de llamada en un objeto con constructor de la introducción de la posibilidad de que heredar objetos ejecutará el código que antes han sido totalmente inicializado.

156voto

Lloyd Puntos 7111

Las reglas de C# son muy diferentes que los de Java y C++.

Cuando usted está en el constructor de un objeto en C#, que el objeto existe en una forma totalmente inicializado (no "construido"), como su forma plenamente tipo derivado.

namespace Demo
{
    class A 
    {
      public A()
      {
        System.Console.WriteLine("This is a {0},", this.GetType());
      }
    }

    class B : A
    {      
    }

    // . . .

    B b = new B(); // Output: "This is a Demo.B"
}

Esto significa que si usted llama a una función virtual desde el constructor de Una, se resolverá a cualquier anulación en B, si se proporciona.

Incluso si usted intencionalmente configurar a y B como el presente, comprender el comportamiento del sistema, usted podría estar en un choque posterior. Dicen que se llama funciones virtuales en la B del constructor, "sabiendo" que sería manejado por B o a, según corresponda. Luego pasa el tiempo, y alguien decide que necesitan para definir C, y anular algunas de las funciones virtuales allí. De repente B del constructor termina de llamar a código en C, lo que podría conducir a bastante sorprendente comportamiento.

Es probablemente una buena idea para evitar que las funciones virtuales en los constructores de todos modos, ya que las reglas son tan diferentes entre C#, C++y Java. Sus programadores no saben qué esperar!

84voto

Ilya Ryzhenkov Puntos 5731

Razones de la advertencia ya se han descrito, pero, ¿cómo iba a solucionar el aviso? Usted tiene que sellar, ya sea de la clase o miembro virtual.

  class B
  {
    protected virtual void Foo() { }
  }

  class A : B
  {
    public A()
    {
      Foo(); // warning here
    }
  }

Usted puede sello de clase:

  sealed class A : B
  {
    public A()
    {
      Foo(); // no warning
    }
  }

O usted puede sellar método Foo:

  class A : B
  {
    public A()
    {
      Foo(); // no warning
    }

    protected sealed override void Foo()
    {
      base.Foo();
    }
  }

16voto

Alex Lyman Puntos 7480

En C#, una clase base constructor se ejecuta antes de la clase derivada constructor, por lo que cualquier instancia de los campos que una clase derivada puede utilizar en el posiblemente reemplazado miembro virtual no se inicializan todavía.

Tenga en cuenta que esto es simplemente una advertencia para que pague la atención y asegúrese de que está todo correcto. De hecho hay casos de uso para este escenario, usted sólo tiene que documentar el comportamiento del miembro virtual que se puede no utilizar ninguna de campos de instancia declaró en una clase derivada por debajo de donde el constructor de llamada es.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X