Delegate specifics
I have problem with a delegate in a class on a project that I'm working on. The class is a GUI Component that accepts both a label and a value. The idea here is that a user can specify a label, and then link in a value from anywhere (more specifically, that value's ToString Method) so that every time that value is updated, the GUI Component is as well. This is the basics of how it is set up:
public delegate string GUIValue();
public class GUIComponent
{
GUIValue value = null; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, GUIValue Value)
{
this.text = Text;
this.value += Value;
}
public void Update()
{
this.text = this.label + this.value();
}
}
And then I call it like this
GUIComponent component = new GUIComponent("Label: ",
new GUIValue(this.attribute.ToString));
The Code compiles correctly, and the component does display, and displays the initial value for the attribute given to it, however, it does not update whenever the attribute value is changed.
My question is whether or not I even have this set up right in the first place, and if so why it would not be working. My initial thought is that it only accepts th开发者_如何转开发e first value return by the ToString method, since it doesn't take any arguments, but can anyone verify that?
This code:
new GUIValue(this.attribute.ToString)
will not cause the method to be called every time the attribute changes. You'd have to store the delegate and call it each time someone changes "attribute". Something like:
private event GUIValue attributeChanged = () => this.attribute.ToString();
private String attribute;
// This is a property that sets the value of attribute
public String Attribute { get { return attribute; } set { attribute = value; attributeChanged(); } }
// Now you can initialize the component using:
// GUIComponent component = new GUIComponent("Label: ", this.attributeChanged);
A delegate needs to be invoked.
What you have there is value
referencing this.attribute.ToString
method.
This means that when you'll call this.value()
then the that function will be called.
When you change the value of this.attribute
you probably did so by referencing it to a different object containing a different value.
So i guess that what you're experiencing is that every time you call update()
then the old value appears. That is because that the old object isn't destroyed by the garbage collector because you are holding a reference to it via the delegate.
When you changed the attribute's value then the GUI delegate still holds the old object's method and not the new one's.
You have half of it. I think what's happening is that although you can initially get the value, your GuiComponent is not told by whatever class actually has the method given as the GUIValue delegate that the value has actually changed and to re-get it. The normal method of telling other objects that something has happened is an event, to which other objects "subscribe" by passing in delegates that will be run when the event is raised.
Here's how I would structure your code:
public interface IHaveAValueYouNeed
{
string ValueGetter();
event EventArgs ValueChanged;
}
public class GUIComponent
{
public delegate string ValueGetter();
ValueGetter getter; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, IHaveAValueYouNeed getter)
{
this.text = Text;
this.getter += getter.ValueGetter;
getter.ValueChanged += ValueUpdatedHandler;
}
public void Update()
{
this.text = this.label + this.value();
}
public void ValueUpdatedHandler(object sender, EventArgs e)
{
Update();
}
}
Now, when you pass in an implementation of the interface to the component, the component will exchange delegates with the instance, getting a reference to its ValueGetter and subscribing to its event. Implementations of IHaveAValueYouNeed should then raise the event when the value changes (either directly, or because something that would change a calculated value produced by the getter has changed). This way, the object controlling the value can tell people interested in that value that it has changed.
Why not just use ToString?
public class GUIComponent
{
object value = null; // The value linked in
string label = ""; // The label for the value
string text = ""; // The label and value appended together
public GUIComponent(string Text, object Value)
{
this.text = Text;
this.value = Value;
}
public void Update()
{
this.text = this.label + this.value.ToString();
}
}
精彩评论