为什么 WPF 支持绑定到对象的属性,但不支持绑定字段? [英] Why does WPF support binding to properties of an object, but not fields?

查看:33
本文介绍了为什么 WPF 支持绑定到对象的属性,但不支持绑定字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 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() { ... }

我像这样在 ViewModel 中公开一个属性:

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}" />

当我运行应用程序时,我在输出窗口中看到错误,表明找不到 Total 属性.我检查并仔细检查并正确输入.我突然想到这些错误特别表明找不到属性".因此,向结构添加属性使其工作正常.但这对我来说似乎很奇怪,WPF 无法处理与字段的单向绑定.从语法上讲,您在代码中以相同的方式访问它们,并且只为 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;}
}

它现在将按照您认为的方式运行.如果你希望它是一个 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();
    }
}

这篇关于为什么 WPF 支持绑定到对象的属性,但不支持绑定字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆