views:

48

answers:

3

Becase we can't use generic type attributes, are there any substitute solutions? Maybe an example is helpful to discuss:

public abstract class ErrorHandler { }
public class AccessHandler : ErrorHandler { }
public class ConnectionHandler : ErrorHandler { }
public class OtherHandler : ErrorHandler { }

public class CoHandler<T> : Attribute where T : ErrorHandler
{
    public T GetHandler()
    {
        return default(T);  // just an example
    }

}
public enum Errors
{
    [CoHandler<AccessHandler>()]
    Access,
    [CoHandler<ConnectionHandler>()]
    Connection,
    [CoHandler<OtherHandler>()]
    Other
}
+3  A: 

Well, you may accept type or type name argument in attribute constructor. For example

[CoHandler(typeof(AccessHandler))]

or

[CoHandler("[Fully or partially qualified type name]")]

Former is simpler to use while later is useful when you don't want to take or can't take dependency on the assembly containing the type.

BTW, return default(T); will always return null, I hope its just for illustration purpose. Here's the example, how you can use type argument:

public class CoHandler : Attribute
{
    private Type _Type;

    public CoHandler(Type type)
    {
       _Type = type;

       // Use reflection to validate type argument to see if it has 
       // inherited from ErrorHandler  and check if its has parameterless 
       // constructor
    }

    public ErrorHandler GetHandler()
    {
        return (ErrorHandler)Activator.CreateInstance(_Type);
    }

}
VinayC
But I will lose `where T : ErrorHandler` check, right? Is there any idea to keep this compile-time checking?
Danny Chen
@Danny - yes, that's correct. You can't have compiler doing the this checking. You may get a compile/build time check by using static analysis tool such as FxCop and then writing custom rule but IMO, it is too muck work and you would most likely to catch incorrect type usage in first round of testing it self.
VinayC
A: 

You can always make explicit versions of the generic class, and then use those.

public class AccessHandlerAttribute : CoHandler<AccessHandler> {}
public class ConnectionHandlerAttribute : CoHandler<ConnectionHandler> {}
public class OtherHandlerAttribute : CoHandler<OtherHandler> {}

public enum Errors
{
    [AccessHandler]
    Access,
    [ConnectionHandler]
    Connection,
    [OtherHandler]
    Other
}
Cameron MacFarland
But in this solution, I need to define several attributes, instead of using generic types.
Danny Chen
I didn't say it was a *good* solution...
Cameron MacFarland
A: 

In your example, by changing the enum into a class, and using interfaces instead of attributes, you could achieve the same functionality.

You could test for interface implementation with as, and you can also write extension methods for the interface.

You can also implement methods on the Errors classes, giving more range than plain old enums.

{
    Errors error = Errors.Access;
    var handler = error.GetHandler();   
}

public abstract class ErrorHandler { }
public class AccessHandler : ErrorHandler { }
public class ConnectionHandler : ErrorHandler { }
public class OtherHandler : ErrorHandler { }

public interface CoHandler<T>
    where T : ErrorHandler
{
    T GetHandler();
}

public abstract class Errors
{
    public static Errors Access = new AccessError();
    public static Errors Connection = new ConnectionError();

    public abstract ErrorHandler GetHandler();

    private class AccessError : Errors, CoHandler<AccessHandler>
    {
        public override ErrorHandler GetHandler()
        {
            return new AccessHandler();
        }

        AccessHandler CoHandler<AccessHandler>.GetHandler()
        {
            return new AccessHandler();
        }
    }

    private class ConnectionError : Errors, CoHandler<ConnectionHandler>
    {
        public override ErrorHandler GetHandler()
        {
            return new ConnectionHandler();
        }

        ConnectionHandler CoHandler<ConnectionHandler>.GetHandler()
        {
            return new ConnectionHandler();
        }
    }

}
Leon van der Walt
It's the best way, comparing with other answers above.
Danny Chen