设置对象的所有属性时是否会发生事件?

时间:2018-11-06 15:12:58

标签: c# .net events

想象一个简单的POCO

//------------------------------------
//---------------------------main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "myapp.h"
#include "myappview.h"

int main(int argc, char *argv[])
{
        QGuiApplication app(argc, argv);

        qmlRegisterType<MyAppView>("org.myappview", 1, 0, "MyAppView");

        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
        if (engine.rootObjects().isEmpty())
                return -1;

        MyApp myApp;

        return app.exec();
}



//------------------------------------
//---------------------------myappview.h
#include <QObject>

class MyAppView : QObject
{
        Q_OBJECT
        Q_PROPERTY(QString myString READ getMyString NOTIFY myStringChanged)

public:
        MyAppView();
        QString getMyString() { return m_myString; }
        void setMyString(QString newString)
        {
                m_myString = newString;
                emit myStringChanged;
        }

signals:
        void myStringChanged();

private:
        QString m_myString;
}



//------------------------------------
//---------------------------main.qml
import QtQuick 2.0
import QtQuick.Window 2.0

import org.myappview 1.0

Window {
        visible: true

        MyAppView {
                id: backend
        }

        Text {
                text: qsTr(backend.myString)
        }
}



//------------------------------------
//---------------------------myapp.h
#include <QObject>
#include "myappview.h"

class MyApp : QObject
{
        Q_OBJECT
public:
        MyApp();

private:
        MyAppView appView;
        void changeMyStringInAppView()
        {
                // This will automatically update main.qml
                appView.setMyString("This is new string");
        }
}

是否有一种方法可以连接此对象,以便仅在设置所有属性后才触发事件?像InitializeComplete事件之类的?还是有一种方法可以轻松创建这样的事件自定义?

谢谢

4 个答案:

答案 0 :(得分:2)

您可以这样自己实现:

public delegate void AllPropertiesSetDelegate();
public class Test
{
    public delegate void AllPropertiesSetDelegate(object sender, EventArgs args);

    public int Id
    {
        get => _id;
        set
        {
            _id = value;
            CheckAllProperties();
        }
    }
    private int _id;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            CheckAllProperties();
        }
    }
    private string _name;

    private void CheckAllProperties()
    {
        //Comparing Id to null is pointless here because it is not nullable.
        if (Name != null && Id != null)
        {
            AllPropertiesSet?.Invoke(this, new EventArgs());
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        t.AllPropertiesSet += delegate { AllPropsSet(); };
        t.Id = 1;
        t.Name = "asd";
        Console.ReadKey();
    }

    static void AllPropsSet()
    {
        Console.WriteLine("All properties have been set.");
    }
}

亲自查看是否可以使实现的规模小一些/减轻痛苦。

测试代码:

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();
        t.AllPropertiesSet += delegate { AllPropsSet(); };
        t.Id = 1;
        t.Name = "asd";
        Console.ReadKey();
    }

    static void AllPropsSet()
    {
        Console.WriteLine("All properties have been set.");
    }
}

此处介绍了如何使用反射检查所有非值类型的null:

    public static bool AllPropertiesNotNull<T>(this T obj) where T : class
    {
        foreach (var prop in obj.GetType().GetProperties())
        {
            //See if our property is not a value type (value types can't be null)
            if (!prop.PropertyType.IsValueType)
            {
                if (prop.GetValue(obj, null) == null)
                {
                    return false;
                }
            }
        }
        return true;
    }

您可以通过修改CheckAllProperties方法在原始代码中使用它:

    private void CheckAllProperties()
    {
        if (this.AllPropertiesNotNull())
        {
            AllPropertiesSet?.Invoke(this, new EventArgs());
        }
    }

答案 1 :(得分:1)

如果要确保正确创建对象,为什么不创建它,所以创建它的唯一方法是还要设置所有属性。

public class Test{
    public int ID {get; set;}
    public string Name {get; set;}
    public string SomeProperty {get; set;}

    // Constructor
    public Test(int id, string Name, string someProperty)
    {
        this.ID = id;
        this.Name = name;
        this.SomeProperty = someProperty;
    }
}

答案 2 :(得分:0)

这是@fstam答案的变体。它将其更改为event的全部值,并使调用检查属性的方法更加简洁。如果您支持我,请支持@fstam。

首先上课:

public class TestPropertyCheck
{
    public event EventHandler AllPropertiesSet;

    public int Id
    {
        get => _id;
        set
        {
            _id = value;
            CheckAllProperties(PropertyNames.Id);
        }
    }
    private int _id;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            CheckAllProperties(PropertyNames.Name);
        }
    }
    private string _name;

    public string Address
    {
        get => _address;
        set
        {
            _address = value;
            CheckAllProperties(PropertyNames.Address);
        }
    }
    private string _address;

    private void CheckAllProperties(PropertyNames propName)
    {
        propertiesSet |= propName;
        if (propertiesSet == PropertyNames.AllProps)
        {
            AllPropertiesSet?.Invoke(this, new EventArgs());
        }

    }

    private PropertyNames propertiesSet = PropertyNames.None;

    [Flags]
    private enum PropertyNames
    {
        None = 0,
        Id = 0x01,
        Name = 0x02,
        Address = 0x04,
        AllProps = Id | Name | Address,
    }
}

然后是测试程序

public static class PropertyCheckTester
{
    public static void Test()
    {
        var test = new TestPropertyCheck();
        test.AllPropertiesSet += AllPropertiesSet;
        Debug.WriteLine("Setting Name");
        test.Name = "My Name";
        Debug.WriteLine("Setting ID");
        test.Id = 42;
        Debug.WriteLine("Setting Address");
        test.Address = "Your address goes here";

    }

    public static void AllPropertiesSet(object sender, EventArgs args)
    {
        Debug.WriteLine("All Properties Set");
    }
}

输出:

Setting Name
Setting ID
Setting Address
All Properties Set

答案 3 :(得分:0)

对于具有完整属性的普通类而言,这样的事件相当容易实现(以下代码来自@fstam的答案,部分来自头部,告诉我是否有问题,或以伪代码对其进行威胁):

public class Test
{
    public EventHandler InitializeCompleted;

    int _id;
    public int Id
    {
        get => _id;
        set
        {
            _id = value;
            CheckAllProperties();
        }
    }

    string _name;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            CheckAllProperties();
        }
    }

    HashSet<string> _initialized = new HashSet<string();

    void CheckAllProperties([CallerMemberName] string property = null)
    {
        if(_initialized == null) // ignore calls after initialization is done
            return;
        _initialized.Add(property);
        if(_initialized.Count == 2) // all properties setters were called
        {
            _initialized = null;
            InitializeCompleted?.Invoke(this, EventArgs.Empty);
        }
    }
}

使用反射可以使任务更加简单:您可以获取属性计数器(无需在CheckAllProperties中保留该数字),标记必须包含/排除的属性。如果您决定这样做,请不要忘记使用惰性模式,即仅对类型(而不是对每个实例)使用一次。