On May 19, 2008, at 12:08 PM, Peter Duniho wrote:
[...]
However, _with_ reflection we can do much of the same kinds of things that Obj-C does, without knowing in advance the classes that might use the NSUndoManager class.

One advantage I see in Cocoa is that, because classes may respond to selectors that they didn't even declare, NSUndoManager can simply set a temporary variable, and then catch a selector to be saved away for later invocation. This makes the reflection aspect ("introspection" in Objective-C) more transparent.

Right. I'm glad you see that. Another place to look for the same type of thing is Distributed Objects in Obj-C. It's another piece of the frameworks that use the ability to catch invocations "in flight". In that case, in order to serialize the method and arguments, send the data across the network or between threads, unserialize on the other side, and invoke in that other machine/process/thread.

An approach in C# that is still reasonably close to the Obj-C version would be to instead pass a method name and an array of arguments (so, the syntax isn't identical, but comparable):

undoManager.prepareWithInvocationTarget(this, "setColor", object[] { mColor });

Then the method would look something like this (warning: email code, uncompiled, untested):

void prepareWithInvocationTarget(object target, string name, object[] args)
   {
       Type[] argTypes = new Type[args.Length];
       MethodInfo targetMethod;

       for (int iarg = 0; iarg < args.Length; iarg++)
       {
           argTypes[iarg] = args[iarg].GetType();
       }

       targetMethod = target.GetType().GetMethod(name, argTypes);

// save targetMethod and args in an appropriate data structure for
       // later retrieval and invocation
       // ...code omitted for brevity
   }

Thanks very much for the example. The same sort of facility (getting method from name) is available in Objective-C, of course, and Andreas Mayer replied on the list an interesting example of how he uses that in AppleScript handling. What C# seems to be missing is the reverse facility (going from a compiled method call back out to name + arguments), which my sample NSUndoManager code demonstrated.

Interestingly, given your earlier remarks about the desirability of compile time checking, in Objective-C [[undoManager prepareWithInvocationTarget:self] setColor:mColor] is type checked. The compiler knows about the -setColor: method declaration it has seen and can check that mColor is an appropriate type. (Because Obj-C still lets you call any method on any object the result of bad typing here will be a warning rather than an error, but Obj-C programmers generally learn to pay attention to and fix all warnings.) Whereas I suspect that when you are using the reflection facilities in C# in the way you are above, that there is no type checking being performed. Is that correct?

That is one of the advantages of having the dynamism built into the language runtime rather than a reflection API built on top. Another advantage is that code can be written that doesn't need to know whether reflection is being used or not. In the Distributed Objects case, for instance, it is very common to pass around proxies as arguments to code that doesn't have any idea that the methods it is calling on those argument objects actually get forwarded somewhere else entirely.

In reality, I would (and have) more likely implement an undo manager that uses anonymous methods. Then all you're saving to your undo state is a delegate that does what you want (assumes the "property" semantic I posted):

   undoManager.AddUndo(delegate { color = mColor; });

This is more idiomatic in C# and wouldn't need all that messy reflection stuff. Executing the undo is a simple matter of invoking the delegate that was passed, a simple one-line operation that reads like a method call (note that the use of the name "delegate" is very different in C# than in Cocoa...C# "delegate" is more like a function pointer than an actual object to which some selector has been delegated).

Like I said earlier, I don't know C#, but this doesn't appear to me what the code would actually look like. The trouble is that the whole point is we want to be able to undo a previous -setColor: call. If mColor is a reference to the "this" object's current color, then at the time that the undo happens, the value of the reference "mColor" will be the _new_ color, not the old color that we want to restore. So that line of code will just set it to itself. What is needed is to store the mColor value as it is at the time the anonymous delegate is created, not at the time the delegate is executed. Is it possible for an anonymous method to have its own instance variables (in this case, to store the old color)? From looking at the docs it doesn't appear to. Nor does it seem possible to do so with named method delegates.

I don't understand the comments saying that you can't do something similar in Java. Java has the same kind of reflection features that C# has. The anonymous method approach wouldn't work, as Java doesn't have anything equivalent to C# delegates, but Java does have interfaces and using those with anonymous types in lieu of anonymous methods is a common Java idiom that works well.

Only with fixed signatures, though. If you wanted to undo (made up example): -setColor:inPattern:atPatternOffset:withAlpha:... then you better hope that your interface includes some similar signature. There are significant flexibility limitations to that approach.

        - Greg
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to