christoph
christoph

Reputation: 1594

property change notification in generic property

Rephrased the question. Scroll down for the original

Ok, maybe I should have given you the whole picture. I have many classes which look like this:

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
}

There are also classes called Transform, Mesh, Collider, Appearance, etc. all derived from Component and all have nothing but properties which are declared as described above. What is important here is to invoke the ComponentChangedEvent. Everything works perfectly, but I was looking for a way where I don't have to rewrite the same logic for each property again and again.

I had a look here and liked the idea of using generic properties. What I came up with looks like this:

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

Then I would use it like this:

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

But I am not able to get the instance where LinearVelocity comes from nor it's name as string. So my question was, if all of this is possible...

But it seems that I have no option other than keep doing it the way I was, writing this logic for each property manually.

Original Question:

Get instance of declaring class from property

I have a class with a property:

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

In another context I have something like this:

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

Then, in DoSomething I need to get fooInstance from having nothing but parameter. From the context, it is save to assume that not any integers are passed into DoSomething, but only public properties of 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
}

Is that possible at all? Using reflection, or maybe a custom attribute on the property Bar?

Upvotes: 0

Views: 1676

Answers (4)

svick
svick

Reputation: 244757

There are several ways to deal with implementing INotifyPropertyChanged. You're doing almost the same thing, except you don't implement the interface and raise the event in a different way. But all of the solutions apply for you too.

  1. Like you do, call a method with a string parameter: OnPropertyChanged("Property").
  2. Call a method with a lambda that uses the property: OnPropertyChanged(() => Property). The advantage of this is that it's compile-time checked for typos and refactoring-friendly.
  3. Use caller information to inject the name of the property: OnPropertyChanged(). This will work in C# 5.
  4. Use something like Castle DynamicProxy to create a derived class at runtime that will call the method for you. This means you need to make your properties virtual and that you need to create instances of the class only through Castle.
  5. Use an AOP framework to modify the code of your properties after compilation to call the method.

Upvotes: 2

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236188

Property is just a field, which returns reference to some object on heap (i.e. its address). If property is not of reference type, it returns value of object.

So, when you do something like

DoSomething(fooInstance.Bar);

You just passing address of object Bar to method.

If Bar is reference type (i.e. class). Imagine that Mr.Foo has an address of Mr.Bar (462 for Marion County, Indiana). Mrs.CLR asks Mr.Foo for address of Mr.Bar. And then tells this address to somebody who needs address of Mr.Bar. How somebody will know, that CLR asked Foo about address of Bar? He received only an address 462 for Marion County, Indiana.

In case of value objects (int, double, structs etc), Mr.Foo has a cool mp3 track named Bar. Mrs. CLR creates a copy of that mp3 track and sends it to somebody. How somebody will know, that his mp3 track Bar is a copy of Mr.Foo's track?

So, if you want somebody to know about Mr.Foo, you need to pass an address of Mr.Foo to him:

DoSomething(fooInstance);

With this address somebody can visit Mr.Foo and ask him about address of Mr.Bar, or create a copy of his mp3 track :)

Upvotes: 0

Beenish Khan
Beenish Khan

Reputation: 1583

Why do you want to send just a property to DoSomething, send it the whole object :), so it would become,

DoSomething(fooInstance);

Your function will then accept object instead of parameter. You can use an overload of this function to make sure that old code doesn't break.

Upvotes: 3

there's no way to get fooInstance from parameter. parameter is passed by value, and is only a copy of the value of fooInstance.Bar, it no longer has anything to do with fooInstance

That being said, the obvious solution is to write DoSomething like this

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
}

Upvotes: 0

Related Questions