且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为什么 WPF 支持绑定到对象的属性,但不支持绑定字段?

更新时间:2022-04-15 03:01:17

绑定一般对字段不起作用.大多数绑定部分基于 ComponentModel PropertyDescriptor 模型,该模型(默认情况下)适用于属性.这会启用通知、验证等(这些都不适用于字段).

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

它现在将按照您认为的方式运行.如果你希望它是一个 immutable 结构,那没问题(当然,数据绑定只能是单向的):

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

但是,我首先要质疑为什么这是一个结构.用 .NET 语言编写结构非常少见.请记住,WCFmex"代理层无论如何都会在消费者处将其创建为一个类(除非您使用程序集共享).

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)"):

如果这是对我问题的回答,那么它在很多方面都是错误的.首先,值类型作为变量通常(首先)在堆栈上分配.如果它们被推送到堆上(例如在数组/列表中),则与类的开销没有太大区别——一小部分对象头加上一个引用.结构应该总是small.具有多个字段的东西会过大,并且会破坏您的堆栈或由于 blitting 而导致缓慢.此外,结构应该是不可变的 - 除非您真的知道自己在做什么.

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.

如果您正在访问数据库,与进入进程外并且可能通过网络相比,struct vs class 的速度不是问题.即使它有点慢,与正确处理相比毫无意义 - 即将对象视为对象.

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.

作为 1M 个对象的一些指标:

As some metrics over 1M objects:

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

基于以下(速度差异在于对象分配,而不是字段与属性).所以大约慢了 5 倍,但仍然非常非常快.由于这不会成为您的瓶颈,因此不要过早地优化它!

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();
    }
}