为什么WPF支持绑定到一个对象的属性,而不是字段?字段、绑定、而不是、属性

2023-09-02 11:44:49 作者:深爱不腻゜

我已经得到了通过像这样一个结构绕过状态更新一个WCF服务:

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

[DataContract]
public struct StatusInfo
{
    [DataMember] public int Total;
    [DataMember] public string Authority;
}
...
public StatusInfo GetStatus() { ... }

我在一个视图模型像这样公开​​的属性:

I expose a property in a ViewModel like this:

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

和XAML像这样:

<TextBox Text="{Binding CurrentStatus.Total}" />

当我运行应用程序我在输出窗口,表明总资产无法找到看到错误。我检查和双重检查,我输入正确无误。在它发生,我的错误明确表明,财产无法找到。因此,增加一个属性的结构使得它工作得很好。但是,这似乎很奇怪,我说,WPF不能处理单向绑定字段。语法上访问它们在code一样,它似乎傻必须创建一个自定义的视图模型只是为StatusInfo结构。我错过了一些关于WPF绑定?你可以绑定到一个字段,或者是属性绑定的唯一途径?

When I run the app I see errors in the output window indicating that the Total property cannot be found. I checked and double checked and I typed it correctly. The it occurred to me that the errors specifically indicate that the 'property' cannot be found. So adding a property to the struct made it work just fine. But this seems odd to me that WPF can't handle one-way binding to fields. Syntactically you access them the same in code and it seem silly to have to create a custom view model just for the StatusInfo struct. Have I missed something about WPF binding? Can you bind to a field or is property binding the only way?

推荐答案

绑定的一般的不工作领域。最具约束力的基础,在某种程度上,对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;}
}

现在将像你认为它应该。如果你希望它是一个一成不变结构,这将是确定的(但数据绑定将是当然只能单向):

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

如果这是我的问题的答复,这是错误的,在许多方面。首先,值类型的作为变量的一般分配(第一个)在栈上。如果它们(以阵列/列表例如)压入堆没有从一个类中的开销太大的差别 - 对象首部加上一个参考的一个小一点。结构应始终小。一些与多个字段会过大,而且要么杀了你的筹码,或只是导致缓慢,由于位图传输。此外,结构应该是一成不变的 - unlesss您真的知道你在做什么

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的任何东西了重新presents的对象应该是immuatable。

Pretty much anything that represents an object should be immuatable.

如果你打一个数据库,结构VS级的速度是一个非问题相比,走出去的进程,可能在网络上。即使这是一个有点慢,这意味着没有什么比得到正确的点 - 即治疗对象为对象

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

基于以下(速度差在对象分配,不能在现场VS属性)。因此,关于5X速度较慢,但​​仍非常,非常快。因为这不会成为您的瓶颈,不要prematurely优化这个!

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