Análisis del código fuente de los ejemplos de WPF por parte de Infragistics Company

 C Programming >> Programación C >  >> Tags >> WPF
Análisis del código fuente de los ejemplos de WPF por parte de Infragistics Company

Seguimos analizando varios proyectos de C# para mostrar las capacidades del analizador de código estático, PVS-Studio. En este artículo, proporcionamos resultados de análisis de ejemplos de WPF de Infragistics Company. Infragistics es un importante proveedor de software, fundado en 1989. Obtuvo popularidad principalmente a través del desarrollo de conjuntos de herramientas de interfaz de usuario listos para la empresa para desarrolladores, que se ejecutan en todas las plataformas, incluida .NET.

Nuestra herramienta, el analizador de código estático PVS-Studio 6.00, tenía una serie de diagnósticos de análisis general de C#, que implementamos utilizando nuestra experiencia en el desarrollo del analizador de C++. A partir de PVS-Studio 6.01, estamos creando diagnósticos específicamente para el lenguaje C#. Para empezar, hemos elegido propiedades de dependencia que se usan en proyectos WPF. Esta elección se hizo por una razón:las propiedades de dependencia son bastante difíciles de crear. La dificultad es que es muy fácil cometer un error tipográfico en el código similar del que suele estar compuesto WPF. Hemos desarrollado varios diagnósticos [3044, 3045, 3046, 3047, 3048, 3049] específicamente para el análisis de dependencias de este tipo.

Como sabemos, una de las peculiaridades de DependencyProperty es que cualquier error durante el registro de DependencyProperty puede provocar que un programa se bloquee durante el tiempo de ejecución. Los programadores tienen que corregir estos errores ejecutando el programa una y otra vez; por lo tanto, un programador dedica preciosos minutos y, en suma, horas, buscando errores tipográficos en el código de plantilla de DependencyProperty. Además de esto, el análisis de WPF mostró que no todos los errores se pueden detectar después de la primera ejecución del programa.

El primer tema de prueba para nuestros diagnósticos fue el código de ejemplos de prueba de Infragistics Company. El archivo fue descargado el 2 de febrero aquí; hay 11 proyectos que se pueden descargar como un archivo.

El análisis se realizó con el analizador de código estático, PVS-Studio 6.01.

Errores de WPF

Gran parte de los proyectos están escritos sobre la base de código pre-usado, y es ahí donde el analizador detecta la mayoría de los errores.

Error N1

En el proyecto "IGExtensions.Common.WPF", en el archivo "LambertConformalConic.cs" vimos la siguiente cadena de registro "DependencyProperty":

public static readonly DependencyProperty CentralMeridianProperty
 = DependencyProperty.Register("CentralMeridianProperty",
    typeof(double), typeof(LambertConformalConic),
      new PropertyMetadata(0.0,
        new PropertyChangedCallback(UpdateConstants)));

V3045 WPF:los nombres de la propiedad registrada 'CentralMeridianProperty' y la propiedad 'CentralMeridian' no se corresponden entre sí. LambertConformalConic.cs 130

Como puede ver, durante el registro de DependencyProperty, en su nombre se escribió "CentralMeridianProperty" en lugar de "CentralMeridian". Este error de copia incorrecta del nombre de la variable ocurre con bastante frecuencia, pero es especialmente peligroso por el siguiente hecho:

Para escribir/leer en la propiedad de dependencia del código C#, los programadores crean la siguiente propiedad:

public double CentralMeridian {
  get { return (double)GetValue(CentralMeridianProperty);  }
  set { SetValue(CentralMeridianProperty, value); } 
}

Cuando se direcciona desde marcado xaml, el enlace se escribe para la propiedad "CentralMeridian". WPF es lo suficientemente inteligente como para encontrar la propiedad CentralMeridian y leer el valor original desde allí, pero los cambios en los valores de CentralMeridian no se procesarán.

Error N2

Continuando con el tema de los errores tipográficos en los nombres de las propiedades de dependencia registradas, echemos un vistazo al siguiente error en el archivo "TransverseMercator.cs" del proyecto "IGExtensions.Common.WPF".

public static readonly DependencyProperty CentralMeridianProperty
  = DependencyProperty.Register("LongitudeOrigin", typeof(double),
     typeof(TransverseMercator), new PropertyMetadata(0.0,
       new PropertyChangedCallback(UpdateConstants)));

public double CentralMeridian { .... }

V3045 WPF:los nombres de la propiedad registrada 'LongitudeOrigin' y de la propiedad 'CentralMeridian' no se corresponden entre sí. TransverseMercator.cs 95

Como muestra la práctica, varias propiedades de dependencia se escriben copiando la misma cadena y editándola más tarde. En otras palabras, usando Copiar-Pegar. Muy a menudo, vemos que en el código similar se omite una variable y recibe un nombre diferente, el que estaba más cerca en la lista. Teniendo en cuenta que la lista está en algún lugar del Bloc de notas [Notepad++, Sublime Text y similares] en una ventana diferente, solo puede verificar manualmente si se crearon los objetos requeridos. Es especialmente difícil detectar tales errores porque el código generalmente funciona, pero en realidad, solo parcialmente.

Error N3

La situación con los nombres de las propiedades registradas es bastante clara, pero ¿dónde más puede un programador cometer un error al crear DependencyProperty? Otra variante - está en los tipos de valores que deben contener las propiedades. Este es un ejemplo de este tipo:proyecto "IGExtensions.Common.WPF", archivo "PropertyBrushColorEditor.cs".

public static readonly DependencyProperty BrushColorProperty = 
  DependencyProperty.Register(BrushColorPropertyName, 
    typeof(Brush), typeof(PropertyBrushColorEditor), 
      new PropertyMetadata(null, (sender, e) => 
      {....})
);

public SolidColorBrush BrushColor
{
 get { return (SolidColorBrush)GetValue(BrushColorProperty); }
 set { SetValue(BrushColorProperty, value); }
}

V3046 WPF:el tipo registrado para DependencyProperty no se corresponde con el tipo de la propiedad utilizada para acceder a ella.

Es bueno si no tiene preguntas, en cuanto a por qué no es correcto especificar la clase principal "Brush" durante el registro, y especificar el direccionamiento de la clase heredera "SolidColorBrush" a través de la propiedad "BrushColor". Si no es así, echemos un vistazo a un caso simplificado de tal "juego" con los tipos almacenados.

Considere un caso simple. Creemos un proyecto WPF simple y agreguemos a la clase la siguiente propiedad de dependencia:

public static DependencyProperty MyIndexProperty =
  DependencyProperty.Register("MyIndex", typeof(int),
  typeof(MainWindow), new FrameworkPropertyMetadata(1));

int MyIndex
{
 get { return (int)GetValue(MyIndexProperty); }
 set { SetValue(MyIndexProperty, value); }
}

En marcado xaml escribiremos lo siguiente:

....

Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource = 
               {RelativeSource Mode=Self}}">
<Grid>
  <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <TextBlock Grid.Row="0" Text="{Binding Path=MyIndex}"/>
  <Slider Grid.Row="1" Name="slider1" 
    Value="{Binding Path=MyIndex}" Maximum="100" />
    <Button Grid.Row="2" Click="Button_Click">
      Read value
    </Button>
</Grid>

Y agregue a la clase de ventana el código para presionar el botón:

private void Button_Click(object sender, RoutedEventArgs e)
{
  this.Title = this.MyIndex.ToString(); 
}

Eso es todo. Como puedes ver, todo funciona. Movemos el control deslizante, el número cambia. Haga clic en el botón y el título de la ventana cambiará inmediatamente al valor actual en el control deslizante. Por cierto, y como probablemente notó, TextBlock muestra valores enteros.

Y ahora cambiemos el tipo "int" al tipo de "objeto" común en la DependencyProperty registrada.

public static DependencyProperty MyIndexProperty =
  DependencyProperty.Register("MyIndex", typeof(object),
  typeof(MainWindow), new FrameworkPropertyMetadata(1));

Dejemos el resto sin cambios y volvamos a ejecutar el programa.

El programa comenzó y ahora, cuando movemos el control deslizante, los valores reales se muestran en el TextBlock. Pero no es difícil adivinar que si presionamos el botón, el programa se bloqueará, ya que no podrá convertir un valor real en MyIndexProperty en uno entero en la propiedad MyIndex. Parece una cosa pequeña, pero tuvo consecuencias realmente malas.

Error N4

Hemos proporcionado algunos ejemplos de errores que son comunes para la mayoría de los proyectos (tan triste que rara vez se solucionan), pero hay algunos errores "locales", por ejemplo en el proyecto IGEquityTrading:

public static readonly DependencyProperty
 AxisFinancialIndicatorYTemplateProperty =
  DependencyProperty.Register("AxisFinancialIndicatorYTemplate",
    typeof(DataTemplate),
    typeof(DataChartEx),
    new PropertyMetadata(default(DataTemplate)));

public DataTemplate AxisCategoryYTemplate{
 get { return (DataTemplate)
  GetValue(AxisFinancialIndicatorYTemplateProperty); }
 set { 
  SetValue(AxisFinancialIndicatorYTemplateProperty, value); }
}

V3045 WPF:los nombres de la propiedad registrada para DependencyProperty y de la propiedad utilizada para acceder a ella no se corresponden entre sí. DataChartEx.cs 469

Infragistics avanza en el mismo rake al crear una propiedad con el nombre "AxisCategoryYTemplate", en lugar del nombre registrado "AxisFinancialIndicatorYTemplate".

Error N5

public static readonly DependencyProperty
 FinancialIndicatorSeriesTemplateProperty =
  DependencyProperty.Register("FinancialIndicatorTemplate",
    typeof(DataTemplate),
    typeof(DataChartEx),
    new PropertyMetadata(default(DataTemplate)));

public DataTemplate FinancialIndicatorSeriesTemplate {
 get { return (DataTemplate)
    GetValue(FinancialIndicatorSeriesTemplateProperty); }
 set { 
    SetValue(FinancialIndicatorSeriesTemplateProperty, value); }
}

V3045 WPF:los nombres de la propiedad registrada para DependencyProperty y de la propiedad utilizada para acceder a ella no se corresponden entre sí. DataChartEx.cs 344

En el último caso, el error probablemente ocurrió después de la refactorización, cuando se especificó la variable y se insertó la palabra "Series" en medio de la frase "FinancialIndicatorTemplate". Lo que es más interesante, se cambió en todas partes, incluso en el marcado XAML y en "#region", pero el nombre de la propiedad registrada permaneció sin cambios.

  • ....\Infra\EquityTrading\IGEquityTrading.WPF\App.xaml(123):
  • ....\Infra\EquityTrading\IGEquityTrading.WPF\App.xaml(214):FinancialIndicatorSeriesTemplate="{StaticResource FinancialIndicatorSeriesTemplate}"
  • ....\Infra\EquityTrading\IGEquityTrading.WPF\Controls\DataChartEx.cs(189):var financialIndicator =FinancialIndicatorSeriesTemplate.LoadContent() as Series;
  • ....\Infra\EquityTrading\IGEequityTrading.WPF\Controls\DataChartEx.cs(330):#región FinancialIndicatorSeriesTemplate (Propiedad de dependencia)
  • ....\Infra\EquityTrading\IGEequityTrading.WPF\Controls\DataChartEx.cs(336):public DataTemplate FinancialIndicatorSeriesTemplate
  • ....\Infra\EquityTrading\IGEquityTrading.WPF\Controls\DataChartEx.cs(349):#endregion FinancialIndicatorSeriesTemplate (Propiedad de dependencia)
  • ....\Infra\EquityTrading\IGEequityTrading.WPF\Controls\StockHistoryChart.xaml(646):FinancialIndicatorSeriesTemplate="{StaticResource FinancialIndicatorSeriesTemplate}"

Al mismo tiempo, el nombre registrado "FinancialIndicatorTemplate" no se usa en ninguna parte. Ya sabemos a qué puede conducir esto.

Errores varios de C#

No vimos ningún otro error de WPF en estas compilaciones de Infragistics Company. Como ya se mencionó, la mayoría de los diagnósticos de WPF están diseñados para encontrar errores antes de compilar y ejecutar el proyecto. Estos proyectos con los ejemplos ya han sido revisados ​​por programadores y especialistas en control de calidad. Además, estos proyectos también fueron vistos por los usuarios que pudieron juzgar la calidad y operatividad de la herramienta, trabajando con los ejemplos de prueba. Supongo que si notaron un error, notificaron a los desarrolladores.

Por supuesto, hay otros errores en estas compilaciones además de los de WPF. El analizador emitió varios cientos de advertencias en total. No todos los mensajes indican un error real. Muchas advertencias (por ejemplo, comparar tipo doble con constante), simplemente no son relevantes para este tipo de proyecto. No es un gran problema, porque el analizador proporciona varios mecanismos para suprimir mensajes poco interesantes.

En cualquier caso, hay muchas advertencias, y la mayoría de ellas muestran las anomalías en el código. Estos son errores reales o código "olor". Por lo tanto, recomendamos que los desarrolladores realicen el análisis ellos mismos y examinen todas las advertencias del analizador. En este artículo vamos a echar un vistazo a los más interesantes:

public bool IsValid
{
get {
  var valid = 
    double.IsNaN(Latitude) || double.IsNaN(Latitude) ||
    this.Weather.DateTime == Weather.DateTimeInitial;
  return valid;
 }
}

V3001 Hay subexpresiones idénticas 'double.IsNaN(Latitude)' a la izquierda y a la derecha de '||' operador. WeatherStation.cs 25

Los programadores tienen una vida dura. Deben comprender no solo la programación, sino también las áreas en las que debe funcionar el programa. Resulta que deben comprender el área temática y conocer algunas palabras específicas "Crédito", "Débito", Latitud, "Longitud", por ejemplo, por lo que solo agrega complejidad, especialmente si los conceptos son similares en ortografía. Resulta que por error escribimos comprobaciones de la misma variable:double.IsNaN(Latitude) || double.IsNaN(Latitude).

Siguiente error:

private static int clipSegment(....)
{
 if (xmax > rc.Right && xmax > rc.Right)
 {
   return -1;
 }
}

V3001 Hay subexpresiones idénticas 'xmax> rc.Right' a la izquierda ya la derecha del operador '&&'. Geometría. Geometría.CubicSpline.cs 529

Es algo bastante común:verificar los límites de una variable, pero es bastante fácil cometer un error al escribir símbolos después y dentro de la variable. Para evitar este tipo de errores, debe ceñirse al siguiente patrón:la variable común se escribe desde lados diferentes en las expresiones.

if (xmin < rc.Right && rc.Right < xmax)

Es más difícil cometer un error y se vuelve más legible.

PD Sin embargo, el mismo truco no funcionará en Entity Framework; el programa fallará durante la conversión del código LINQ a SQL. Así que este es el caso :)

Los desarrolladores de Infragistics pensaron demasiado en estos controles. Además del error indicado anteriormente, el mismo error se repite en las siguientes cadenas:

private static int clipSegment(....)
{
  ....
  if (ymin < rc.Top && ymin < rc.Top) // <= here
  ....
  if (ymax > rc.Bottom && ymax > rc.Bottom) // <= and here
  ....
}

Para el diagnóstico V3001 todavía no es suficiente y continúa la expansión. Aquí hay otro ejemplo de su trabajo:

private static bool IsInDesignModeStatic(this Application app)
{
 ....
  if (_isInDesignMode != null && _isInDesignMode.HasValue) 
   return _isInDesignMode.Value;
 ....
}

V3001 Hay subexpresiones idénticas '_isInDesignMode !=null' a la izquierda ya la derecha del operador '&&'. NavegaciónApp.cs 415

En este caso tenemos código redundante, no un error. Esto fue suficiente:

if (_isInDesignMode.HasValue)

Otra advertencia de V3001

void ParagraphSettingsPreviewAdapter_PropertyChanged(
 object sender, PropertyChangedEventArgs e) {
 ....
 if (LineSpacingType == Infrastructure.LineSpacingTypes.Exactly 
  || LineSpacingType == Infrastructure.LineSpacingTypes.Exactly){
 ....
}

V3001 Hay subexpresiones idénticas 'LineSpacingType ==Infrastructure.LineSpacingTypes.Exactly' a la izquierda y a la derecha de '||' operador. ParagraphSettingsPreviewAdapter.cs 268

No está muy claro lo que el programador quiso decir aquí, pero no lo que realmente está escrito.

Pasemos de V3001 a V3010.

Hay un par de llamadas a funciones en el proyecto "IGEarthQuake.WPF".

public MapViewModel() {
  ....
  WeakPropertyChangedListener.CreateIfNecessary(_service, this);
  ....
}

V3010 Se requiere utilizar el valor de retorno de la función 'CreateIfNecessary'. MapViewModel.cs 42

public TimeLineViewModel(){
  ....
  WeakPropertyChangedListener.CreateIfNecessary(_service, this);
  ....
}

V3010 Se requiere utilizar el valor de retorno de la función 'CreateIfNecessary'. TimeLineViewModel.cs 50

En ambos casos se llama a la misma función bastante simple. Veamos su implementación:

public static 
WeakPropertyChangedListener CreateIfNecessary(object source,
IPropertyChangedListener listener){
  INotifyPropertyChanged inpc = source as INotifyPropertyChanged;
  return inpc != null ? 
    new WeakPropertyChangedListener(inpc, listener) : null;
}

Como puede ver, esta característica no trae ningún cambio global, y su resultado tampoco se usa. Así que aquí está la pregunta:¿por qué se llamó? Parece muy sospechoso...

Un ejemplo similar está en el proyecto "IGHospitalFloorPlan.WPF":

private void ParseAllShapefiles() {
  ....
  this.ShapeFilesMaxBounds.Expand(new Thickness(10, 10, 10, 10));
  ....
}

V3010 Se requiere utilizar el valor de retorno de la función 'Expandir'. HospitalView.xaml.cs 52

Su implementación es un poco más complicada, pero finalmente solo devuelve un nuevo objeto que nunca se usa.

Hemos llegado a la mitad del artículo. Echa un vistazo a esta imagen; relájate y luego continuamos.

Uno de los tipos de error más comunes es copiar y pegar mal:

public static EsriMapImageryView 
   GetImageryView(EsriMapImageryStyle imageryStyle){
 ....
  if (imageryStyle ==
    EsriMapImageryStyle.UsaPopulationChange2010Overlay)
 return EsriMapImageryViews.UsaPopulationChange2010Overlay;
  if (imageryStyle ==
    EsriMapImageryStyle.UsaPopulationChange2010Overlay)
 return EsriMapImageryViews.UsaPopulationChange2010Overlay;
 ....
}

V3021 Hay dos sentencias 'si' con expresiones condicionales idénticas. La primera instrucción 'if' contiene el retorno del método. Esto significa que la segunda declaración 'si' no tiene sentido EsriMapImageryView.cs 97

En este caso, el mismo código está bajo la misma condición. En esta etapa, el error es un método de copiar y pegar incorrecto (redundante). Pero después de la refactorización, puede ocurrir que el programador cambie el cuerpo de la función if inferior, que nunca se ejecuta, y se produzca un error en la lógica del programa.

Veamos otros errores que ocurrieron en el código de la empresa Infragistics.

Se emitió la advertencia V3022 para la siguiente cadena:

public static double GenerateTemperature(GeoLocation location){
  ....
  else if (location.Latitude > 10 || location.Latitude < 25) 
  ....
  else if (location.Latitude > -40 || location.Latitude < 10)
  ....
}

public static WeatherCondition GenerateWeatherCondition(....){
  ....
  else if (location.Latitude > 10 || location.Latitude < 25)
  ....
  else if (location.Latitude > -40 || location.Latitude < 10)
  ....
}

Todos los errores son detectados por este diagnóstico:

V3022 Expresión 'ubicación.Latitud> -40 || location.Latitude <10' siempre es cierto. Probablemente el operador '&&' debería usarse aquí.

Que mas podemos decir? Probablemente lo mismo que en la descripción de uno de los errores, encontrado por V3001. Es útil usar este patrón cuando la misma variable se escribe desde ambos lados de la expresión:

if (xmin < rc.Right && rc.Right < xmax)

En este punto dejaremos de examinar los errores del primer nivel y pasaremos al segundo y tercer nivel, porque el mismo número de mensaje, dependiendo de la situación, tiene una prioridad diferente.

El analizador emite avisos de diagnóstico de tercer nivel, cuando no está muy seguro de su exactitud. También el tercer nivel es para aquellos diagnósticos que no son relevantes para todos los proyectos.

En la práctica, las advertencias del tercer nivel muy rara vez son signos de errores reales. A menudo se trata de falsos positivos o mensajes que muestran algún código de olor, que sigue funcionando bastante correctamente. En cualquier caso, si hay tiempo, se deben explorar estos mensajes de diagnóstico y se debe refactorizar el código.

Comencemos con el código que tiene dos funciones idénticas:

// 0 reference
public static double Ramp(double a) {
  return a - Math.Floor(a);
}

// 1 reference
public static double Frac(double a) {
  return a - Math.Floor(a);
}

V3013 Es extraño que el cuerpo de la función 'Rampa' sea totalmente equivalente al cuerpo de la función 'Frac' (28, línea 33). Matemáticas.cs 28

Si la función Frac tiene algún significado, entonces solo en lenguaje Pascal; mientras que Ramp no tiene análogos, o simplemente no los he encontrado. Los contadores de los fragmentos donde se usa esta función hablan por sí solos (ver los comentarios).

Echemos un vistazo a un caso en el que apareció este error en el segundo nivel.

public void StartCurrent()
{
  StartTask("Current");
}
public void StopCurrent()
{
  StartTask("Current");
}

V3013 Es extraño que el cuerpo de la función 'StartCurrent' sea completamente equivalente al cuerpo de la función 'StopCurrent' (503, línea 507). DataViewModel.cs 503

Aparentemente, en el segundo caso, la función "StartTask" se confundió con "StopTask; ambas funciones están presentes en el código y actúan claramente según sus nombres.

Ahora veamos una serie de mensajes relacionados con el siguiente código:

{
  IsUpdating = true;
  ....
  IsUpdating = false;
}

El código similar se puede ver en 4 fragmentos (en cada compilación).

  • V3008 A la variable 'IsUpdating' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:201, 195. GeoRegion.cs 201
  • V3008 A la variable 'IsUpdating' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:212, 205. GeoRegion.cs 212
  • V3008 A la variable 'IsUpdating' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:226, 216. GeoRegion.cs 226
  • V3008 A la variable 'IsUpdating' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:244, 236. GeoRegion.cs 244

Inicialmente, parece que esta variable se utiliza para la comunicación entre hilos. Pero resultó que, en la práctica, esta variable no se encuentra en ninguna parte, excepto en las cadenas para las que se emitió el mensaje de diagnóstico.

Bueno, supongamos que decide utilizar esta variable para la sincronización entre subprocesos. Y luego esta desagradable sorpresa que nos espera. La declaración de variables tiene el siguiente aspecto:

protected bool IsUpdating = false;

Como puede ver, no hay una palabra clave "volátil" y, como resultado, el compilador la optimiza con éxito y funcionará de manera completamente incorrecta.

¿Qué más se encontró en el código? Por ejemplo, algunas evaluaciones adicionales:

Ejemplo 1:

public static void Normalize(....)
{
  var x = rect.X < boundingRect.X ? boundingRect.X : rect.X;
  x = (rect.X + rect.Width) > boundingRect.Right ? 
     boundingRect.X : rect.X;
}

V3008 A la variable 'x' se le asignan valores dos veces seguidas. Quizás esto sea un error. Comprobar líneas:96, 95. RectEx.cs

Ejemplo 2:

private static GradientStopCollection fromInterpolation(....){
 ....
 Color color=ColorTool.FromAHSV(ahsv[0], 
                                ahsv[1], 
                                ahsv[2], 
                                ahsv[3]);
 color = ColorTool.FromARGBInterpolation(min, p, max[i].Color);
 ....
}

V3008 A la variable 'color' se le asignan valores dos veces seguidas. Quizás esto sea un error. Marcar líneas:165, 163. BrushTool.cs

A veces nos encontramos con fragmentos de código muy divertidos:

private void UpdateAutoSavedState() {
  AutoSaved = true;
  AutoSaved = false;
}

V3008 A la variable 'AutoGuardado' se le asignan valores dos veces seguidas. Quizás esto sea un error. Verifique las líneas:691, 690. ShellViewModel.cs 691

Para aquellos que todavía tienen dudas, proporciono declaración de propiedad:

private bool autoSaved;
public bool AutoSaved
{
  get { return autoSaved; }
  set { autoSaved = value; }
}

Y nuevamente no hay "volátil", o algo por el estilo, que hablaría sobre el significado oculto de esta acción.

Pasemos a otro grupo de cadenas con el error V3029:

public void OnPropertyChanged(PropertyChangedEventArgs ea) {
 ....
 var index = this.SelectedBrushCollectionIndex;
 ....
 if (index >= 0) 
  DebugManager.LogData(this.BrushCollectionList[index].ToText());
 if (index >= 0) 
  this.SelectedBrushCollectionIndex = index;
 ....
}

V3029 Las expresiones condicionales de los operadores 'if' situados uno al lado del otro son idénticas. Ver líneas:338, 339.

public static void EnableSeriesMouseDoubleClick(
  this XamGeographicMap geoMap, bool isEnabled = true){
  ....
  if (geoMap != null) geoMap.SeriesMouseLeftButtonDown +=
    OnSeriesMouseLeftButtomDown;
  if (geoMap != null) geoMap.SeriesMouseLeftButtonUp +=
    OnSeriesMouseLeftButtonUp;
  ....
  if (geoMap != null) geoMap.SeriesMouseLeftButtonDown -=
    OnSeriesMouseLeftButtomDown;
  if (geoMap != null) geoMap.SeriesMouseLeftButtonUp -=
    OnSeriesMouseLeftButtonUp;
  ....
}

V3029 Las expresiones condicionales de los operadores 'if' situados uno al lado del otro son idénticas. Verifique las líneas:92, 93. GeoMapAdapter.cs 92

V3029 Las expresiones condicionales de los operadores 'if' situados uno al lado del otro son idénticas. Verificar líneas:100, 101. GeoMapAdapter.cs 100

public void SyncSeriesViewPropertyChanges() {
  if (this.SeriesView != null) 
    this.SeriesView.PropertyUpdated += OnSeriesViewPropertyUpdated;
  if (this.SeriesView != null) 
    this.SeriesView.PropertyChanged += OnSeriesViewPropertyChanged;
}

V3029 Las expresiones condicionales de los operadores 'if' situados uno al lado del otro, son idénticas. Verificar líneas:342, 343. GeoSeriesLayer.cs 342

Como suele decirse, "por si acaso"....

Aunque no se trata de errores, las comprobaciones repetidas abarrotan el código y dificultan su comprensión.

Y aquí está el código redundante, que probablemente apareció durante la refactorización.

public Frame NavigationTarget
{
 get { return (Frame)this.GetValue(NavigationTargetProperty); }
 set {
  var targetFrame = value as Frame;
  if (targetFrame != null)
    this.SetValue(NavigationTargetProperty, value);
 }
}

"valor" ya tiene el tipo Marco, la conversión no tiene sentido. Pero en este caso, es necesario considerar la situación en un sentido más amplio. Infragistics hace la verificación contra nulo cuando escribe en DependencyProperty. Los desarrolladores proporcionaron una función de devolución de llamada "ValidateValueCallback" para comprobaciones de este tipo. Esta función se establece cuando registra una propiedad de dependencia y verifica los valores que se escriben en DependencyProperty.

Conclusión

Una vez más, nuestro Unicornio arcoíris con armadura brillante detectó una cantidad considerable de áreas problemáticas (el artículo no enumera todos los errores que encontramos). Los desarrolladores ahora pueden corregir el código y hacerlo mejor de lo que era... De lo que era cuando se estaba escribiendo... Cuando se estaba probando... Que cuando se reescribió, ejecutó y cuando volvió a fallar una y otra vez, o funcionado de forma distinta a la que debería...

En mi práctica en mi trabajo anterior, hubo tiempos realmente difíciles los fines de semana y las noches, varios días antes de la fecha límite, cuando teníamos que hacer mucho trabajo en un período de tiempo muy corto. Todo el equipo sabía qué hacer, pero por las prisas y el cansancio se tardó más en depurar el código. Es decir. escribimos código, lo ejecutamos y no funciona según lo previsto. Paramos todo, ponemos un punto de interrupción y lo volvemos a ejecutar. Realice todas las acciones repetidamente, establezca el punto de interrupción y verifique cadena por cadena lo que está sucediendo. Saltar de un lado a otro a lo largo del código y revisar los valores en las variables. Pero al final resulta que perdimos una variable o un carácter en la condición... Así es como se pasan 15 minutos buscando un simple error tipográfico durante el Copiar y Pegar.

El análisis de proyectos es solo la punta del enorme iceberg de problemas que ocurren durante la creación de código.

Nadie es inmune a los errores. Incluso escribiendo el código que debería ser ejemplar en la empresa, es imposible evitar errores.

Mi sincero consejo para usted:use el analizador PVS-Studio de forma regular. Tiene todo tipo de características útiles. Por ejemplo, hay un modo en el que los archivos modificados se vuelven a verificar; no es necesario que lo ejecute; el analizador verifica por sí mismo lo que es necesario y emite advertencias cuando es necesario.