Why does WPF support binding to properties of an object, but not fields?

前端 未结 2 2101
感动是毒
感动是毒 2020-11-21 11:37

I\'ve got a WCF service that passes around status updates via a struct like so:

[DataContract]
public struct StatusInfo
{
    [DataMember] public int Total;
         


        
相关标签:
2条回答
  • 2020-11-21 11:57

    I can only guess why they only support properties: perhaps because it seems to be a universal convention in the .NET framework never to expose mutable fields (probably to safeguard binary compatibility), and they somehow expected all programmers to follow the same convention.

    Also, although fields and properties are accessed with the same syntax, data binding uses reflection, and (so I've heard) reflection must be used differently to access fields than to access properties.

    0 讨论(0)
  • 2020-11-21 12:03

    Binding generally doesn't work to fields. Most binding is based, in part, on the ComponentModel PropertyDescriptor model, which (by default) works on properties. This enables notifications, validation, etc (none of which works with fields).

    For more reasons than I can go into, public fields are a bad idea. They should be properties, fact. Likewise, mutable structs are a very bad idea. Not least, it protects against unexpected data loss (commonly associated with mutable structs). This should be a class:

    [DataContract]
    public class StatusInfo
    {
        [DataMember] public int Total {get;set;}
        [DataMember] public string Authority {get;set;}
    }
    

    It will now behave as you think it should. If you want it to be an immutable struct, that would be OK (but data-binding would be one-way only, of course):

    [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;
        }
    }
    

    However, I would first question why this is a struct in the first place. It is very rare to write a struct in .NET languages. Keep in mind that the WCF "mex" proxy layer will create it as a class at the consumer anyway (unless you use assembly sharing).


    In answer to the "why use structs" reply ("unknown (google)"):

    If that is a reply to my question, it is wrong in many ways. First, value types as variables are commonly allocated (first) on the stack. If they are pushed onto the heap (for example in an array/list) there isn't much difference in overhead from a class - a small bit of object header plus a reference. Structs should always be small. Something with multiple fields will be over-sized, and will either murder your stack or just cause slowness due to the blitting. Additionally, structs should be immutable - unlesss you really know what you are doing.

    Pretty much anything that represents an object should be immuatable.

    If you are hitting a database, the speed of struct vs class is a non-issue compared to going out-of-process and probably over the network. Even if it is a bit slower, that means nothing compared to the point of getting it right - i.e. treating objects as objects.

    As some metrics over 1M objects:

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

    based on the following (the speed difference is in object allocation, not field vs property). So about 5x slower, but still very, very quick. Since this is not going to be your bottleneck, don't prematurely optimise this!

    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();
        }
    }
    
    0 讨论(0)
提交回复
热议问题