想象一个简单的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事件之类的?还是有一种方法可以轻松创建这样的事件自定义?
谢谢
答案 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
中保留该数字),标记必须包含/排除的属性。如果您决定这样做,请不要忘记使用惰性模式,即仅对类型(而不是对每个实例)使用一次。