¿Por qué WPF admite el enlace a las propiedades de un objeto, pero no a los campos?

Tengo un servicio WCF que pasa las 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 este:

 public class ServiceViewModel : ViewModel { public StatusInfo CurrentStatus { get{ return _currentStatus; } set { _currentStatus = value; OnPropertyChanged( () => CurrentStatus ); } } } 

Y XAML me gusta así:

  

Cuando ejecuto la aplicación, veo errores en la ventana de salida que indican que no se puede encontrar la propiedad Total. Revisé y comprobé 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 lo hizo funcionar 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 tonto tener que crear un modelo de vista personalizado solo para la estructura de StatusInfo. ¿Me he perdido algo sobre el enlace de WPF? ¿Se puede vincular a un campo o la propiedad es vinculante de la única manera?

La unión generalmente no funciona en los campos. La mayoría de los enlaces se basan, en parte, en el modelo de PropertyDescriptor ComponentModel, que (de forma predeterminada) funciona en las propiedades. Esto permite notificaciones, validación, etc. (ninguno de los cuales funciona con campos).

Por más razones de las que puedo entrar, los campos públicos son una mala idea. Deben ser propiedades, hecho. Del mismo modo, las estructuras mutables son una muy mala idea. No menos importante, protege contra la pérdida inesperada de datos (comúnmente asociada con estructuras mutables). Esto 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 crees que debería. Si quieres que sea una estructura inmutable , estaría bien (pero el enlace de datos sería de una sola vía, 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 en primer lugar. Es muy raro escribir una estructura en lenguajes .NET. Tenga en cuenta que la capa de proxy “mex” de WCF lo creará como una clase para el consumidor de todos modos (a menos que use el ensamblaje compartido).


En respuesta a la respuesta “por qué usar estructuras” (“desconocido (google)”):

Si esa es una respuesta a mi pregunta, está mal de muchas maneras. Primero, los tipos de valor como variables se asignan comúnmente (primero) en la stack. Si se insertan en el montón (por ejemplo, en una matriz / lista), no hay mucha diferencia en la sobrecarga de una clase, un pequeño encabezado de objeto más una referencia. Las estructuras siempre deben ser pequeñas . Algo con múltiples campos será sobredimensionado, y matará a su stack o simplemente causará lentitud debido al blitting. Además, las estructuras deben ser inmutables, a menos que realmente sepa lo que está haciendo.

Prácticamente todo lo que representa un objeto debe ser inmutable.

Si está accediendo a una base de datos, la velocidad de la estructura frente a la clase no es un problema en comparación con salir fuera de proceso y probablemente a través de la red. Incluso si es un poco más lento, eso no significa nada en comparación con el objective de hacerlo bien, es decir, tratar objetos como objetos.

Como algunas métricas sobre objetos de 1M :

 struct/field: 50ms class/property: 229ms 

en función de lo siguiente (la diferencia de velocidad está en la asignación de objetos, no en el campo frente a la propiedad). Entonces, aproximadamente 5 veces más lento, pero aún así , muy rápido . Dado que este no será su cuello de botella, ¡no optimice esto 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 s = new List(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 c = new List(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(); } } 

Solo puedo adivinar por qué solo admiten propiedades: quizás porque parece ser una convención universal en el framework .NET nunca exponer campos mutables ( probablemente para salvaguardar la compatibilidad binaria ), y de alguna manera esperaban que todos los progtwigdores siguieran la misma convención.

Además, aunque se accede a los campos y propiedades con la misma syntax, el enlace de datos utiliza la reflexión y (por lo tanto, he oído) que la reflexión debe usarse de forma diferente para acceder a los campos que para acceder a las propiedades.