views:

419

answers:

3

Looking for a way to pass an associative array to a method. I'm looking to rewrite an Actionscript tween package in C# but running into trouble with "associative" arrays/objects. Normally in Actionscript I might do something like:

public function tween(obj:DisplayObject, params:Object = null):void {
    if (params.ease != null) {
        //do something
    }
    //...
}

And this could be called like:

tween(this, {ease:'out', duration:15});

I'm looking for a way to do the same in C#. So far, I've gathered my options to be:

a) creating a class or struct to define the possible param keys and types and pass that

b) pass the parameters as generic type

tween(frameworkElement, new {ease = 'out', duration = 15});

assuming

public static void tween(FrameworkElement target, object parameters);

and figure out some way to use that in the tween function (I'm not sure how to separate the key=value's given that object. any ideas?)

c) create a Dictionary<string, object> to pass the parameters into the tween function

Any other ideas or sample code? I'm new to C#.

Edit

Took me all day to figure this out:

"Anonymous types cannot be shared across assembly boundaries. The compiler ensures that there is at most one anonymous type for a given sequence of property name/type pairs within each assembly. To pass structures between assemblies you will need to properly define them."

+1  A: 

I'm a fan of option (b) myself - passing an anonymous type and parsing out the values using reflection.

I've seen others achieve the same thing using lambda expressions. The calling syntax would be:

tween(frameworkElement, ease => "out", duration => 15);

And the declaration would be something along the lines of:

public static void tween(FrameworkElement target, params Expression<Func<object>>[] parameters) { ... }

The idea is that you can take a variable number of "functions which return object". You then parse the name of the parameter out of each Expression<TDelegate>, and invoke each one to get its value.

I don't think this is any better than reflecting over an anonymous type, but it's another approach to consider.

Update

I have actually written about the idea of passing associative arrays as dictionaries on my blog, here and here.

Matt Hamilton
That's an entertaining idea. It seems a little bit of an abuse (seeing as the "functions" you're defining don't actually do anything except pass your hardcoded data around) but it's neat.
mquander
Yeah I prefer anonymous types myself.
Matt Hamilton
+1  A: 

The Dictionary is a reasonable approach. It might look like this:

public static void tween(FrameworkElement target, IDictionary<string, object> parameters)
{
    if (parameters.ContainsKey("ease")) {
        //do something
    }
}

Mind that when you pass the values in, you can use shortened collection initialization syntax to make it easier, like:

tween(something, new Dictionary<string, object> { { "ease", "out" }, { duration, 15 } });

This would be a pretty quick way to do roughly "the same thing" as the ActionScript code, but it's not really great C#. The C#-ish way to do it would be to make a real TweenParameters struct or class with properties for each possible parameter, and then create one to pass into tween(). This is generally considered more maintainable, since all the "available" properties are obvious to a caller without looking into the internals of tween(), and because it catches errors at compile-time that a string comparison wouldn't notice until runtime, like typoing "duration" "duratoin".

mquander
I do like the idea of maintaining a "proper" list of available params in a struct.
Typeoneerror
+1  A: 

EDIT: This is basically the mechanism that HtmlHelper extensions use in ASP.NET MVC. It's not original with me.


I'd favor a hybrid approach that has two different signatures. Note: I haven't tried this and there may be a conflict between the two signatures so you may have to give the second method a slightly different name to allow the compiler to choose between them, but I don't think so.

public static void tween( FrameworkElement target, object parameters )
{
    return tween( target, new ParameterDictionary( parameters ) );
}

public static void tween( FrameworkElement target,
                          ParameterDictionary values )
{
    if (values.ContainsKey( "ease" ))
    {
      ....
    }
}

Then you have a ParameterDictionary class that uses reflection on the anonymous type and sets up the dictionary.

public class ParameterDictionary : Dictionary<string,object>
{
    public ParameterDictionary( object parameters )
    {
         if (parameters != null)
         {
             foreach (PropertyInfo info in parameters.GetType()
                                                     .GetProperties())
             {
                 object value = info.GetValue(parameters,null);
                 this.Add(info.Name,value);
             }
         }
    }
}

This gives you both ease of use and ease of consumption -- the "ugly" reflection stuff is wrapped up in the single constructor for the dictionary rather than in your method. And, of course, the dictionary can be used over and over for similar purposes with the reflection code only written once.

tvanfosson
This is awesome. I think I could run with this.
Typeoneerror
I tried this out, but I'm getting a System.MethodAccessException error on the info.GetValue() call.
Typeoneerror
Other than deriving from Dictionary<string,object> I just tried this out in SnippetCompiler and didn't have any problems with it. I'm calling the code with: var dict = new ParameterDictionary( new { ID = 1, Value = "test" } );
tvanfosson
Can you post a code sample?
tvanfosson
added a sample in the post
Typeoneerror
What version of the framework are you using? I tried your sample in SnippetCompiler with .NET 3.5 sp1 with no problems.
tvanfosson
Have you tried using the GetProperties() method without the BindingFlags? They shouldn't be needed as the default is to get the public instance properties. Note, too, that I added a check for a null argument.
tvanfosson
FWIW -- I decided to use it myself instead of RouteValueDictionary in my HtmlHelperExtensions and they still pass all my unit tests.
tvanfosson
Yes, I did. Still getting the MethodAccessException on that call with this message:<>f__AnonymousType0`2.get_easeThanks for your help anyway. Bit of a noob so I've probably got something wrong.
Typeoneerror
The code posted above is exactly the same as what I'm using and I tested your example in SnippetCompiler with no errors. I'm at a loss to explain why it doesn't work for you. I'm using VS2008/.NET 3.5 but I've tested with SnippetCompiler Live 2008 Ultimate Edition (Alpha) as well.
tvanfosson
Is your email address on your web site? I can send you my code for the class and tests.
tvanfosson
I think I figured it out. My loop was in a different assembly, and I think it was failing because anonymous objects are internal by default, so I couldn't access it there. All a bit over my head right now!
Typeoneerror