Re: Cocoa et al as HCI usability problem
Re: Cocoa et al as HCI usability problem
- Subject: Re: Cocoa et al as HCI usability problem
- From: Greg Titus <email@hidden>
- Date: Mon, 19 May 2008 14:05:03 -0700
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 (email@hidden)
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:
This email sent to email@hidden