¿Por qué WPF admite el enlace a las propiedades de un objeto, pero no a los campos?
Tengo un servicio WCF que transmite actualizaciones de estado a través de una estructura como esta:
[DataContract]
public struct StatusInfo
{
[DataMember] public int Total;
[DataMember] public string Authority;
}
...
public StatusInfo GetStatus() { ... }
Expongo una propiedad en un ViewModel como esta:
public class ServiceViewModel : ViewModel
{
public StatusInfo CurrentStatus
{
get{ return _currentStatus; }
set
{
_currentStatus = value;
OnPropertyChanged( () => CurrentStatus );
}
}
}
Y XAML así:
<TextBox Text="{Binding CurrentStatus.Total}" />
Cuando ejecuto la aplicación veo errores en la ventana de salida que indican que no se puede encontrar la propiedad Total. Revisé y revisé dos veces y lo escribí correctamente. Se me ocurrió que los errores indican específicamente que no se puede encontrar la 'propiedad'. Entonces, agregar una propiedad a la estructura hizo que funcionara bien. Pero me parece extraño que WPF no pueda manejar el enlace unidireccional a los campos. Sintácticamente, accede a ellos de la misma manera en el código y parece una tontería tener que crear un modelo de vista personalizado solo para la estructura StatusInfo. ¿Me he perdido algo sobre el enlace de WPF? ¿Se puede vincular a un campo o la propiedad vinculante es la única forma?
El enlace generalmente no funciona con los campos. La mayor parte de los enlaces se basan, en parte, en el PropertyDescriptor
modelo ComponentModel, que (de forma predeterminada) funciona con propiedades. Esto permite notificaciones, validación, etc. (ninguno de los cuales funciona con campos).
Por más razones de las que puedo explicar, los campos públicos son una mala idea. Deberían ser propiedades, de hecho. Asimismo, las estructuras mutables son una muy mala idea. No menos importante, protege contra pérdidas inesperadas de datos (comúnmente asociadas con estructuras mutables). Esta debería ser una clase:
[DataContract]
public class StatusInfo
{
[DataMember] public int Total {get;set;}
[DataMember] public string Authority {get;set;}
}
Ahora se comportará como cree que debería. Si desea que sea una estructura inmutable , estaría bien (pero el enlace de datos sería solo unidireccional, por supuesto):
[DataContract]
public struct StatusInfo
{
[DataMember] public int Total {get;private set;}
[DataMember] public string Authority {get;private set;}
public StatusInfo(int total, string authority) : this() {
Total = total;
Authority = authority;
}
}
Sin embargo, primero me preguntaría por qué se trata de una estructura. Es muy raro escribir una estructura en lenguajes .NET. Tenga en cuenta que la capa de proxy "mex" de WCF la creará como una clase en el consumidor de todos modos (a menos que utilice el uso compartido de ensamblados).
En respuesta a la respuesta "por qué utilizar estructuras" ("desconocido (google)"):
Si esa es una respuesta a mi pregunta, está equivocada en muchos sentidos. Primero, los tipos de valor como variables comúnmente se asignan (primero) en la pila. Si se colocan en el montón (por ejemplo, en una matriz/lista), no hay mucha diferencia en la sobrecarga con respecto a una clase: una pequeña parte del encabezado del objeto más una referencia. Las estructuras siempre deben ser pequeñas . Algo con múltiples campos tendrá un tamaño excesivo y acabará con su pila o simplemente causará lentitud debido al blitting. Además, las estructuras deben ser inmutables, a menos que realmente sepas lo que estás haciendo.
Prácticamente cualquier cosa que represente un objeto debería ser inmutable.
Si está accediendo a una base de datos, la velocidad de struct vs class no es un problema en comparación con salir del proceso y probablemente a través de la red. Incluso si es un poco más lento, eso no significa nada comparado con el punto de hacerlo bien, es decir, tratar los objetos como objetos.
Como algunas métricas sobre 1 millón de objetos:
struct/field: 50ms
class/property: 229ms
basado en lo siguiente (la diferencia de velocidad está en la asignación de objetos, no en el campo frente a la propiedad). Aproximadamente 5 veces más lento, pero aún muy, muy rápido . Dado que este no será su cuello de botella, ¡no lo optimice prematuramente!
using System;
using System.Collections.Generic;
using System.Diagnostics;
struct MyStruct
{
public int Id;
public string Name;
public DateTime DateOfBirth;
public string Comment;
}
class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string Comment { get; set; }
}
static class Program
{
static void Main()
{
DateTime dob = DateTime.Today;
const int SIZE = 1000000;
Stopwatch watch = Stopwatch.StartNew();
List<MyStruct> s = new List<MyStruct>(SIZE);
for (int i = 0; i < SIZE; i++)
{
s.Add(new MyStruct { Comment = "abc", DateOfBirth = dob,
Id = 123, Name = "def" });
}
watch.Stop();
Console.WriteLine("struct/field: "
+ watch.ElapsedMilliseconds + "ms");
watch = Stopwatch.StartNew();
List<MyClass> c = new List<MyClass>(SIZE);
for (int i = 0; i < SIZE; i++)
{
c.Add(new MyClass { Comment = "abc", DateOfBirth = dob,
Id = 123, Name = "def" });
}
watch.Stop();
Console.WriteLine("class/property: "
+ watch.ElapsedMilliseconds + "ms");
Console.ReadLine();
}
}