【问题标题】:property change notification in generic property通用属性中的属性更改通知
【发布时间】:2012-04-20 16:35:51
【问题描述】:

重新表述了这个问题。向下滚动查看原文

好吧,也许我应该给你完整的图片。我有很多类看起来像这样:

public class Movement : Component
{
    private Vector3 linearVelocity;
    public Vector3 LinearVelocity
    {
        get
        {
            return linearVelocity;
        }
        set
        {
            if (value != linearVelocity)
            {
                linearVelocity = value;
                ComponentChangedEvent<Movement>.Invoke(this, "LinearVelocity");
            }
        }
    }

    // other properties (e.g. AngularVelocity), which are declared exactly
    // the same way as above
}

还有一些名为 Transform、Mesh、Collider、Appearance 等的类,它们都派生自 Component,并且除了上述声明的属性外,什么都没有。这里重要的是调用ComponentChangedEvent。一切都很完美,但我一直在寻找一种方法,我不必一次又一次地为每个属性重写相同的逻辑。

我看了here 并喜欢使用泛型属性的想法。我想出的看起来像这样:

public class ComponentProperty<TValue, TOwner>
{
    private TValue _value;

    public TValue Value
    {
        get
        {
            return _value;
        }
        set
        {
            if (!EqualityComparer<TValue>.Default.Equals(_value, value))
            {
                _value = value;
                ComponentChangedEvent<TOwner>.Invoke(
                    /*get instance of the class which declares value (e.g. Movement instance)*/,
                    /*get name of property where value comes from (e.g. "LinearVelocity") */);
            }
        }
    }

    public static implicit operator TValue(ComponentProperty<TValue, TOwner> value)
    {
        return value.Value;
    }

    public static implicit operator ComponentProperty<TValue, TOwner>(TValue value)
    {
        return new ComponentProperty<TValue, TOwner> { Value = value };
    }
}

然后我会这样使用它:

public class Movement : Component
{
    public ComponentProperty<Vector3, Movement> LinearVelocity { get; set; }
    public ComponentProperty<Vector3, Movement> AngularVelocity { get; set; }
}

但我无法获得 LinearVelocity 来自的实例,也无法将其名称作为字符串。所以我的问题是,如果这一切都是可能的......

但我似乎别无选择,只能像以前一样继续这样做,为每个属性手动编写此逻辑。

原问题:

从属性中获取声明类的实例

我有一个有属性的类:

public class Foo
{
    public int Bar { get; set; }
}

在另一种情况下,我有这样的事情:

Foo fooInstance = new Foo();
DoSomething(fooInstance.Bar);

然后,在DoSomething 中,我需要从除了parameter 之外什么都没有得到fooInstance。从上下文来看,可以假设没有任何整数被传递到 DoSomething,而只是 ints 的公共属性。

public void DoSomething(int parameter)
{
    // need to use fooInstance here as well,
    // and no, it is not possible to just pass it in as another parameter
}

这可能吗?使用反射,或者属性 Bar 上的自定义属性?

【问题讨论】:

  • 你必须重新考虑你的设计。这是不可能的。
  • 我不明白你的要求......你为什么不能把Foo 作为参数?
  • 为什么不能作为其他参数传入?
  • 感谢大家的回答。也许我应该更改问题的标题,因为我的问题主要与通用属性更改通知的实现有关,svick 已经回答了这个问题

标签: c# reflection


【解决方案1】:

为什么你只想向 DoSomething 发送一个属性,将整个对象发送给它:),所以它会变成,

DoSomething(fooInstance);

然后您的函数将接受对象而不是参数。您可以使用此函数的重载来确保旧代码不会中断。

【讨论】:

    【解决方案2】:

    有几种方法可以处理实现INotifyPropertyChanged。您正在做几乎相同的事情,只是您没有实现接口并以不同的方式引发事件。但所有解决方案也适用于您。

    1. 和你一样,调用带有字符串参数的方法:OnPropertyChanged("Property")
    2. 使用使用以下属性的 lambda 调用方法:OnPropertyChanged(() =&gt; Property)。这样做的好处是它可以在编译时检查拼写错误并且便于重构。
    3. 使用caller information 注入属性名称:OnPropertyChanged()。这适用于 C# 5。
    4. 使用 Castle DynamicProxy 之类的东西在运行时创建一个派生类,该类将为您调用该方法。这意味着您需要将您的属性设为virtual,并且您只需要通过 Castle 创建该类的实例。
    5. 使用 AOP 框架在编译后修改属性的代码以调用方法。

    【讨论】:

    • 谢谢,我会看看这些。实现 INotifyPropertyChanged 也应该可以解决问题。调用者信息(和其他 C# 5 内容)看起来很有趣,但这是为了将来。
    【解决方案3】:

    无法从parameter 获取fooInstanceparameter是传值的,只是fooInstance.Bar的值的一个拷贝,和fooInstance已经没有任何关系了

    话虽如此,显而易见的解决方案是这样写DoSomething

    public void DoSomething(Foo parameter)
    {
        // need to use fooInstance here as well,
        // and no, it is not possible to just pass it in as another parameter
    }
    

    【讨论】:

      【解决方案4】:

      属性只是一个字段,它返回对堆上某个对象的引用(即它的地址)。如果属性不是引用类型,则返回对象的值。

      所以,当你做类似的事情时

      DoSomething(fooInstance.Bar);
      

      您只是将对象 Bar 的地址传递给方法。

      如果 Bar 是引用类型(即类)。假设 Mr.Foo 的地址是 Mr.Bar(462 代表印第安纳州马里恩县)。 Mrs.CLR 向 Mr.Foo 询问 Mr.Bar 的地址。然后把这个地址告诉需要Mr.Bar地址的人。怎么会有人知道,CLR 向 Foo 询问了 Bar 的地址?他只收到了印第安纳州马里恩县的地址462

      对于值对象(int、double、structs 等),Mr.Foo 有一个很酷的 mp3 曲目,名为 Bar。 CLR 夫人创建了该 mp3 曲目的副本并将其发送给某人。怎么会有人知道,他的 mp3 曲目 Bar 是 Mr.Foo 曲目的副本?

      所以,如果你想让某人知道 Mr.Foo,你需要将 Mr.Foo 的地址传递给他:

      DoSomething(fooInstance);
      

      有了这个地址,有人可以拜访 Mr.Foo 并询问他关于 Mr.Bar 的地址,或者创建他的 mp3 曲目的副本:)

      【讨论】:

      • 其实参数更像是一个函数;有一个 getter 和 setter 来返回/设置属性的本地值。如果此属性用于引用类型,那么它将简单地传递对象的新引用,并且该引用将指向内存中的同一对象。由于这是一个值类型,它会将值的新副本传回给您。
      • @ShawnL,我知道,该属性实际上是 get_Name 和 set_Name 函数 :) 只是想简化
      • 感谢lazyberezovsky,精彩的解释:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      • 2015-10-29
      • 1970-01-01
      • 2017-05-21
      • 2021-04-16
      相关资源
      最近更新 更多