Yuck
Yuck

Reputation: 50855

Determine if a generic type has a corresponding implementation

I have a series of static methods to modify a collection then return the modified collection:

private static IEnumerable<Invoice> ResolveProxies(IEnumerable<Invoice> e) {
    // do something to e
    return e;
}

private static IEnumerable<Person> ResolveProxies(IEnumerable<Person> e) {
    // do something to e
    return e;
}

In another part of the application there is a method to decide if a collection is of a certain type, so that it can be converted to that type and have its corresponding ResolveProxies method called:

public static GridModel<T> ToGridModel<T>(this GridModel gridModel) {
    // gridModel.Data is just IEnumerable
    var collection = gridModel.Data as IEnumerable<T> ?? new List<T>();

    return new GridModel<T> {
        Data = EvaluateDynamicProxies(collection),
        Total = gridModel.Total
    };
}

private static IEnumerable<T> EvaluateProxies<T>(IEnumerable<T> collection) {
    if (collection is IEnumerable<Invoice>) {
        var enumeration = (collection as IEnumerable<Invoice>);
        return ResolveProxies(enumeration) as IEnumerable<T>;
    }
    if (collection is IEnumerable<Person>) {
        var enumeration = (collection as IEnumerable<Person>);
        return ResolveProxies(enumeration) as IEnumerable<T>;
    }

    // proxy resolution isn't needed so return the unchanged collection
    return collection;
}

Having such repetitive conditional logic is bad code smell. I'm struggling to come up with some way to mark particular types so that I know they have a corresponding proxy resolver method. Something like this perhaps:

public interface IProxyResolver<out T> where T:IEnumerable<T> {
    T ResolveProxies();
}

But how would I use this? In effect I need a way to ask the compiler:

  1. Does T have a matching ResolveProxies method?
  2. What is the name of the class or method that resolves proxies for T so that I can get an instance of it and call it?

Upvotes: 1

Views: 170

Answers (2)

phoog
phoog

Reputation: 43056

You could use an inversion of control (IOC) framework. For example, my team uses Castle Windsor. You can register services (usually interfaces) and types that provide the services. It has some nice generics resolution, so you can do things like this:

interface IProxyResolver<T> { /* whatever */ }
class ProxyResolver<T> : IProxyResolver<T> { /* ... */ }
class PersonProxyResolver : ProxyResolver<Person> { }
class InvoiceProxyResolver : ProxyResolver<Invoice> { }

then, you can summon these types like this:

void SomeMethodThatNeedsAProxyResolver<T>(T obj)
{
    var resolver = ioc.Resolve<IProxyResolver<T>>();
    //...
}

If you've regsitered the classes above, when T is Person or Invoice, you get the correct non-generic subclass of ProxyResolver; if it is any other type, you get the default generic superclass. Of course, you can structure things differently; if you need a specific proxy resolver for every type, that's possible too.

Upvotes: 1

Chris Shain
Chris Shain

Reputation: 51369

How about using a custom attribute? This is how custom serializers are selected, etc.

You'd start by defining the Attribute class:

public class ProxyResolverAttribute : Attribute {
    public Type ResolverType { get; set; }
    public ProxyResolver(Type resolverType) { ResolverType = resolverType; }
}

and then put that on the type contained, e.g.

[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class Invoice ... { ... }

then use reflection to see if the generic type used in the collection specifies a proxy resolver type:

// Untested, beware of bugs
var enumerationGenericType = enumeration.GetType().GetGenericArguments().FirstOrDefault();
var resolverAttribute = enumerationGenericType.GetType().GetCustomAttributes(TypeOf(ProxyResolverAttribute)).FirstOrDefault();

if (resolverAttribute != null) {
    var resolverType = resolverAttribute.ResolverType;
    // instanciate something of resolverType here
}

EDIT: Reading the comments, if you don't want to apply the attributes to the contained objects, I'd suggest creating custom classes which inherit List and apply the attribute there, e.g.

[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class InvoiceList : List<Invoice>   

Upvotes: 1

Related Questions