Подтвердить что ты не робот

Почему WPF поддерживает привязку к свойствам объекта, но не полям?

У меня есть служба WCF, которая передает обновления статуса через такую ​​структуру:

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

Я выставляю свойство в ViewModel следующим образом:

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? Можете ли вы привязываться к полю или связывать свойство единственным способом?

4b9b3361

Ответ 1

Связывание обычно не работает с полями. Большая часть привязки, в частности, основана на модели ComponentModel PropertyDescriptor, которая (по умолчанию) работает над свойствами. Это позволяет получать уведомления, проверять и т.д. (Ни один из которых не работает с полями).

По другим причинам, чем я могу заниматься, публичные поля - плохая идея. Они должны быть свойствами, фактом. Аналогично, изменчивые структуры - это плохая идея очень. Не в последнюю очередь это защищает от непредвиденных потерь данных (обычно связанных с изменчивыми структурами). Это должен быть класс:

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

Теперь он будет вести себя так, как вы думаете. Если вы хотите, чтобы это была неизменяемая структура, это было бы нормально (но привязка данных была бы только в одну сторону, конечно):

[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. Имейте в виду, что прокси-сервер WCF "mex" будет создавать его как класс у потребителя в любом случае (если вы не используете совместное использование).


В ответ на ответ "зачем использовать structs" ( "unknown (google)" ):

Если это ответ на мой вопрос, во многих случаях это неправильно. Во-первых, типы значений как переменных обычно выделяются (сначала) в стеке. Если они попадают в кучу (например, в массиве/списке), нет большой разницы в накладных расходах от класса - небольшой бит заголовка объекта плюс ссылка. Структуры всегда должны быть малыми. Что-то с несколькими полями будет чрезмерно крупным и будет либо убивать ваш стек, либо просто вызвать медленность из-за блуждания. Кроме того, структуры должны быть неизменными - если вы действительно не знаете, что делаете.

Довольно многое, что представляет объект, должно быть бескомпромиссным.

Если вы ударяете базу данных, скорость struct vs class не является проблемой по сравнению с выходом из процесса и, вероятно, по сети. Даже если это немного медленнее, это ничего не значит по сравнению с тем, как правильно это сделать, то есть обрабатывать объекты как объекты.

Как некоторые показатели над объектами 1M:

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

на основе следующего (разница в скорости в распределении объектов, а не в поле vs). Так что примерно в 5 раз медленнее, но все же очень, очень быстро. Поскольку это не будет вашим узким местом, преждевременно не оптимизируйте это!

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

Ответ 2

Я могу только догадываться, почему они поддерживают только свойства: возможно, потому что это универсальное соглашение в платформе .NET никогда не выставлять изменяемые поля (вероятно для защиты бинарной совместимости), и они как-то ожидали, что все программисты будут следовать тому же соглашению.

Кроме того, хотя поля и свойства доступны с одним и тем же синтаксисом, привязка данных использует отражение, и (так что я слышал) отражение должно использоваться по-разному для доступа к полям, чем для доступа к свойствам.