views:

45

answers:

2

Hey all,

I have a need to convert a number of different objects and I'd like to avoid writing a converter class for each one. Each of the objects inherits from a base class and I need to use the Id to get the Description (which is handled in my call to my CacheManager).

For each class (I have about 30 of them) I have the following code written:

object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    Dictionary<int, string> codes = CacheManager.CodeLookup<CourtEventCode>();
    int id = 0;
    string result = string.Empty;

    if (int.TryParse(value.ToString(), out id) && id > 0)
    {
        if (codes.ContainsKey(id))
        {
            result = codes[id];
        }
        else
        {
            result = "Unknown";
        }
    }

    return result;
}

In the above example, CourtEventCode represents the converter for this one class. Is there a way I can derive that class from the targetType input of IValueConverter.Convert instead of having to essentially copy-and-paste this class two dozen times?

Thanks in advance,
Sonny

+1  A: 

Yes, you can call CacheManager.CodeLookup using reflection.

Based on the code you've shared, it will be something like this:

Type containingType = typeof (CacheManager);
var method = containingType.GetMethod("CodeLookup", 
    BindingFlags.Static | BindingFlags.Public, null, new Type[0], new ParameterModifier[0]);
var concreteMethod = method.MakeGenericMethod(targetType);
Dictionary<string,int> codes = (Dictionary<string,int>)concreteMethod.Invoke(null, null);

Perhaps you will want to cache the concreteMethod instance for each targetType if you are using the method a lot, reflection can be costly in terms of performance.

Edit: When the method is overloaded, in order to match a specific overload; use the GetMethod overload that allows you to specify the exact parameters, an pass in an empty array (since the overload you want to call has no parameters). Code example has been updated.

driis
I've run into another hitch... CodeLookup is an overloaded method and containingType.GetMethod is throwing an exception for an ambiguous match. How do I specify to the reflector to use the method that accepts no arguements?
Sonny Boy
Nevermind. I think I've got it.
Sonny Boy
I updated the answer.
driis
+1  A: 

This answer is an alternative to generics.

You could use a code generator to help speed things up by creating all the classes in one go. Create a blank 'TT' file, (You won't see it in the new item list, just type the extension in manually.) click OK on the warning dialog, and paste this into it. From there, play around until the output file looks right. The output file will be recreated each time you save the TT file.

// The code in this CS file is auto-generated.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;

namespace WpfApplication
{

<#
    string[] classes = new string[]
        {"CourtEventCode", "SomeOtherCode", "WhatElseIsThere"};

    foreach (string classname in classes)
    {
#>
    public class <#= classname #>ValueConverter : IValueConverter
    {
        object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Dictionary<int, string> codes = CacheManager.CodeLookup< <#= classname #> >();
            int id = 0;
            string result = string.Empty;

            if (int.TryParse(value.ToString(), out id) && id > 0)
            {
                if (codes.ContainsKey(id))
                {
                    result = codes[id];
                }
                else
                {
                    result = "Unknown";
                }
            }

            return result;
        }

        // Implement the rest of IValueConverter
    }

<# } #>
}
YotaXP
Yota, this is the approach I have actually taken but when I needed to go and change the base class I then have to update all of the (already) generated code. Not ideal.
Sonny Boy