Reflection 在D中透明地同步对象中的任意属性

Reflection 在D中透明地同步对象中的任意属性,reflection,synchronization,operator-overloading,d,Reflection,Synchronization,Operator Overloading,D,假设我有一门课是这样的: class Gerbil{ int id; float x,y,z; } 让我们进一步说,这是实时模拟的一部分,其中我有一个服务器/客户端设置,并且我更改了服务器端的一个属性: //... gerbil.x = 9.0; //... 现在,我想将此更改发送到客户端以同步世界状态。然而,问题是我可能有大量的沙鼠,这些沙鼠也可能有很长的属性列表,而不仅仅是这里描述的x,y,z 我的问题是:有没有一种方法可以透明地拦截这些属性分配,并从中编译差异? 通过阅

假设我有一门课是这样的:

class Gerbil{
    int id;
    float x,y,z;
}
让我们进一步说,这是实时模拟的一部分,其中我有一个服务器/客户端设置,并且我更改了服务器端的一个属性:

//...
gerbil.x = 9.0;
//...
现在,我想将此更改发送到客户端以同步世界状态。然而,问题是我可能有大量的沙鼠,这些沙鼠也可能有很长的属性列表,而不仅仅是这里描述的x,y,z

我的问题是:有没有一种方法可以透明地拦截这些属性分配,并从中编译差异?

通过阅读D参考资料,我得到的印象是,
opAssign
可能是正确的,只是实际上没有使用它的示例?()我想它看起来像这样,但我只是从臀部开枪:

void opAssign(string name)(float val){ //Just guessing here
     if(name in floatProps){
         if(isServer){
             changedProps.push(this.id, name, val);
         }
         floatProps[name] = val;
     }
}
然后当我们这样做时,会调用opAssign:

gerbil.x = 9.0; //Same as  gerbil.opAssign!("x")(9.0)  ??
除了可能的错误语法之外,这是朝着正确方向迈出的一步吗?正确的语法是什么?性能如何?看起来很慢吧?有没有更快、更“直接”的方法

这里我真正想避免的是复杂的设置,如:

gerbil.incProp(Prop.X, 9.0);

谢谢你的时间。

重载opAccess(),就像重载C++中的赋值运算符。它用于指定对象本身,而不是其成员之一。它真的不会做你想做的事。我相信你能得到的最接近的是房产:

class Gerbil
{
public:

    @property int id()
    {
        return _id;
    }

    @property id(int newID)
    {
        //... Do whatever interception you want.
        _id = newID;
    }

    @property float x()
    {
        return _x;
    }

    @property x(float newX)
    {
        //... Do whatever interception you want.
        _x = newX;
    }

    @property float y()
    {
        return _y;
    }

    @property y(float newY)
    {
        //... Do whatever interception you want.
        _y = newY;
    }

    @property float z()
    {
        return _z;
    }

    @property z(float newZ)
    {
        //... Do whatever interception zou want.
        _z = newZ;
    }

private:

    int _id;
    float _x, _y, _z;
}
@property
启用属性语法,以便您可以像使用变量一样使用函数。所以

//...
auto copyOfGerbilX = gerbil.x; //translates to gerbil.x()
gerbil.x = 9.0;  //translates to gerbile.x(9.0)
//...
现在是合法的,即使
x
是一个函数而不是一个变量。您可以在函数中插入任何需要的特殊处理代码。因为用于访问变量的语法就像它们是公共成员变量一样,所以您可以自由地重构代码,在类定义中使它们成为属性或公共成员变量之间进行切换(假设您没有尝试过像获取他们的地址这样的操作,因为对于变量作为函数来说,这并不意味着相同的事情)

但是,如果你想要的是一种不必自己完成所有这些函数的通用方法,那么就没有直接的构造方法。我相信你可以使用编译时反射和字符串混合或模板混合来完成,它们会查看变量列表,然后为你生成每个属性函数。如何无论如何,对于每个函数,额外的处理代码必须基本相同,并且您必须小心,生成的代码确实是您想要的。我确信这是可行的,但我必须在这个问题上花一点时间来产生可行的解决方案

要生成这样的代码,您需要查看编译时反射和代码生成。我会仔细考虑这样生成代码,而不是手工编写。这应该是可行的,但不一定很容易,调试可能很有趣,如果您必须这样做的话我很好地使用了D模板和混合来获得正确的结果


但本质上,您需要的是使用@property函数,这样您就可以添加处理程序代码,然后可能使用编译时反射和mixin为您生成代码,但是生成这样的代码是一种相当高级的技术,因此您可能希望等到您对D有了更丰富的经验后再尝试基于Jonathan的回答,我在我的许多库中使用了如下代码:

public template property(string name, T) {
    mixin(`protected T _`~name~`;` ~
      propertyGetter!(name, T) ~ propertySetter!(name, T));
}
public template property(string name, T, T def)
{
   mixin(`protected T _`~name~` = `~def.stringof~`;` ~
      propertyGetter!(name, T) ~ propertySetter!(name, T));
}
template propertyGetter(string name, T) {
    enum propertyGetter = `public T `~name~`(){ return _`~name~`; }`;
}
template propertySetter(string name, T) {
    enum propertySetter = `public typeof(this) `~name~`(T value){ _`~name~` = value;`~
              `/* notify somebody that I've changed here */`~
              `return this; }`;
}
mixin字符串有点难看,但它们保留了正确的行数

我将属性添加到类中,如下所示:

class Gerbil {
    mixin property!("id", int);
    mixin property!("x", float);
    mixin property!("y", float, 11.0);  // give this one a default value
}

如果需要,可以向propertySetter模板添加一些代码,通知某种监视器它已更改(传递id、属性名称和新值)。然后监视器可以将此信息传输到服务器端的相应监视器,该监视器将找到具有正确id的对象,并将指定的属性设置为新值。

我明白了。属性和混合听起来很有趣。我将尝试这样做,并在需要时使用手动截取代码。谢谢!哇!现在我更喜欢。:)好的,谢谢你的例子!我不太了解
template
enum
的用法。你能创建函数枚举吗?看起来很酷:)这就是所谓的同名模板(如果您注意到,枚举与模板同名)。实例化时,模板将替换为枚举的值。这是一种在编译时生成值的方法。在本例中,他正在构建用于字符串混合的字符串。