2011-10-07 11 views
7

Con el código de abajo, ReSharper plantea una 'llamada miembro virtual en el constructor' advertencia:Llamada de miembro virtual en el constructor: ¿por qué una está bien y la otra no?

public class UserDetailViewModel : Screen 
{ 
    public UserDetailViewModel() 
    { 
     // DisplayName is a virtual member of Screen 
     DisplayName = "User Details"; 
    } 
} 

Mientras que si se cambia el código así que es como esta, la advertencia desaparece:

public class UserDetailViewModel : Screen 
{ 
    public UserDetailViewModel() 
    { 
     SetName(); 
    } 

    private void SetName() 
    { 
     DisplayName = "User Details"; 
    } 
} 

¿Por qué uno levanta una advertencia y el otro no? ¿Es la segunda forma de alguna manera correcta, o está más allá del límite de lo que ReSharper puede detectar como potencialmente peligroso?

Respuesta

13

Es solo una limitación de ReSharper. Se aplica un montón de heurísticas para encontrar un código que pueda advertir, pero no encontrará todo. Hacerlo requeriría resolver el problema de detención. No es factible.

La lección aquí es bastante simple:
la ausencia de una advertencia no significa que no haya nada que advierta.

+0

Gracias jalf. Para evitar el problema, ¿es aceptable cambiar 'SetName()' para que sea público y llamarlo después de la instanciación, por ejemplo: 'userDetailViewModel.SetName();'? ¿O hay una mejor solución? (Esta debería ser una pregunta nueva, lo sé) – Town

+0

Es posible encontrar que el constructor puede potencialmente llamar a un miembro virtual sin detener el análisis. –

+0

@Town 'new UserDetailViewModel(). SetName()' no es una buena opción porque hace que el que llama tenga la responsabilidad de llamar a 'SetName()'. Preferiría ignorar la advertencia en su lugar. –

1

La advertencia se da debido al problema que puede surgir en la siguiente situación

  • Un objeto de un tipo derivado de UserDetailViewModel, diga "ConcreteModel` se está construyendo

  • ConcreteModel anulaciones Screen.DisplayName. En el método de configuración de la propiedad depende de que el ConcreteModel haya sido completado, digamos que tiene acceso a otro miembro que se inicializó en el constructor.

En este caso, el código anterior emitirá una excepción.

La forma correcta de resolver esto es declarando DisplayName como sealed en UserDetailViewModel. Ahora puede estar seguro de que está bien ignorar la advertencia.

El siguiente ejemplo lo demuestra. Descomentar las líneas en Der provoca un error de compilación.

class Base 
{ 
    public virtual string DisplayName { get; set; } 
} 

class Der : Base 
{ 
    public Der() 
    { 
     // ok to ignore virtual member access here 
     DisplayName = "Der"; 
    } 
    public override sealed string DisplayName { get; set; } 
} 

class Leaf : Der 
{ 
    private string _displayName; 
    public Leaf() 
    { 
     _displayName = "default"; 
    } 
    //override public string DisplayName 
    //{ 
    // get { return _displayName; } 
    // set { if (!_displayName.Equals(value)) _displayName = value; } 
    //} 
} 
3

En Caliburn.Micro, es común que para establecer el DisplayName reemplazando el método OnInitialize(), por ejemplo

protected override void OnInitialize() 
    { 
     DisplayName = "User Details"; 
     base.OnInitialize(); 
    } 

OnInitialize() solo se llama una vez, y ya no se genera una advertencia en Resharper. Vea también here.

+0

+1, Gracias Erik. – Town

Cuestiones relacionadas