Christopher
Christopher

Reputation: 10627

Can I consolidate these methods using C# Generics?

I have this code in my base controller in an MVC app:

protected LookUpClient GetLookupClient() {
    return new LookUpClient(CurrentUser);
}

protected AdminClient GetAdminClient() {
    return new AdminClient(CurrentUser);
}

protected AffiliateClient GetAffiliateClient() {
    return new AffiliateClient(CurrentUser);
}

protected MembershipClient GetMembershipClient() {
    return new MembershipClient(CurrentUser);
}

protected SecurityClient GetSecurityClient() {
    return new SecurityClient();
}

protected ChauffeurClient GetChauffeurClient() {
    return new ChauffeurClient(CurrentUser);
}

Can I somehow consolidate it using a generic method?

Update: SecurityClient()'s different constructor is deliberate. It does not take a user.

Upvotes: 2

Views: 186

Answers (4)

BenAlabaster
BenAlabaster

Reputation: 39836

You can condense them all down to:

protected T GetClient<T>(params object[] constructorParams) 
{
    return (T)Activator.CreateInstance(typeof(T), constructorParams);
}

Call it using:

AdminClient ac = GetClient<AdminClient>(CurrentUser);
LookupClient lc = GetClient<LookupClient>(CurrentUser);
SecurityClient sc = GetClient<SecurityClient>();

etc.

If you had all of your clients utilizing the CurrentUser (currently your example suggests that SecurityClient does not), you could remove the parameter from the method and have just:

protected T GetClient<T>()
{
    return (T)Activator.CreateInstance(typeof(T), CurrentUser);
}

Which simplifies your calls:

AdminClient ac = GetClient<AdminClient>();

But then you lose the ability to use it on clients that don't require the CurrentUser context...

Addendum: In response to your comment regarding the requirement of a parameterless constructor. I've got some demo code that I've tested to prove there isn't a requirement for a parameterless constructor:

public class UserContext
{
    public string UserName { get; protected set; }
    public UserContext(string username)
    {
        UserName = username;
    }
}
public class AdminClient
{
    UserContext User { get; set; }
    public AdminClient(UserContext currentUser)
    {
        User = currentUser;
    }
}
public class SecurityClient
{
    public string Stuff { get { return "Hello World"; } }
    public SecurityClient()
    {
    }
}

class Program
{
    public static T CreateClient<T>(params object[] constructorParams)
    {
        return (T)Activator.CreateInstance(typeof(T), constructorParams);   
    }
    static void Main(string[] args)
    {
        UserContext currentUser = new UserContext("BenAlabaster");
        AdminClient ac = CreateClient<AdminClient>(currentUser);
        SecurityClient sc = CreateClient<SecurityClient>();
    }
}

This code runs without any exceptions and will create the AdminClient which doesn't have a paremeterless constructor, and will also create the SecurityClient which only has a parameterless constructor.

Upvotes: 6

gsharp
gsharp

Reputation: 27927

I try to avoid using reflection when it comes to generics, so if its possible to redesign your XYclient classes with a common base class or an interface i would do that. sample:

public interface IClient
{
    CurrentUser CurrentUser { get; set; }
}

public class LookUpClient : IClient
{
    public CurrentUser CurrentUser {get;set;}
}

public class AffiliateClient : IClient
{
    public CurrentUser CurrentUser { get; set; }
}


class Program
{
    private static T GetClient<T>() where T : IClient, new()
    {
        T client = new T();
        client.CurrentUser = CurrentUser;
        return client;
    }

    static void Main(string[] args)
    {
        var lookup = GetClient<LookUpClient>();
        var affiliate = GetClient<AffiliateClient>();
    }
}

Upvotes: 0

Robaticus
Robaticus

Reputation: 23157

I had a thought about using dynamic to solve this. If you could change your clients to expose CurrentUser as a property, you could potentially do something like this:

    protected T GetClient<T>(int CurrentUser) where T : new()
    {
        T client = new T();
        dynamic retval = client;
        retval.CurrentUser = CurrentUser;
        return retval;  
    }

I don't think this is any better than the suggestions above, but just presented as a different approach.

Upvotes: 1

Lee
Lee

Reputation: 144136

You could use reflection, although this depends on the created type having a matching constructor:

protected T GetClient<T>()
{
    ConstructorInfo ci = typeof(T).GetConstructor(new[] { typeof(User) });
    return (T)ci.Invoke(new object[] { this.CurrentUser });
}

Upvotes: 0

Related Questions