a11smiles
a11smiles

Reputation: 1258

C# Functions With Overloaded Methods

Okay, to show what my code looks like (and this works, but isn't necessarily pretty):

    public delegate Response Func<R1>(ref R1 out1);
    public delegate Response Func<T1, R1>(T1 in1, ref R1 out1);
    public delegate Response Func<T1, T2, R1>(T1 in1, T2 in2, ref R1 out1);
    public delegate Response Func<T1, T2, T3, R1>(T1 in1, T2 in2, T3 in3, ref R1 out1);
    public delegate Response Func<T1, T2, T3, T4, R1>(T1 in1, T2 in2, T3 in3, T4 in4, ref R1 out1);
    public delegate Response Func<T1, T2, T3, T4, T5, R1>(T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, ref R1 out1);
    public delegate Response Func<T1, T2, T3, T4, T5, T6, R1>(T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1);


    public static Response Query<R1>(Func<R1> method, ref R1 out1)
    {
        return QueryAll<object, object, object, object, object, object, R1>(method, null, null, null, null, null, null, ref out1);
    }

    public static Response Query<T1, R1>(Func<T1, R1> method, T1 in1, ref R1 out1)
    {
        return QueryAll<T1, object, object, object, object, object, R1>(method, in1, null, null, null, null, null, ref out1);
    }

    public static Response Query<T1, T2, R1>(Func<T1, T2, R1> method, T1 in1, T2 in2, ref R1 out1)
    {
        return QueryAll<T1, T2, object, object, object, object, R1>(method, in1, in2, null, null, null, null, ref out1);
    }

    public static Response Query<T1, T2, T3, R1>(Func<T1, T2, T3, R1> method, T1 in1, T2 in2, T3 in3, ref R1 out1)
    {
        return QueryAll<T1, T2, T3, object, object, object, R1>(method, in1, in2, in3, null, null, null, ref out1);
    }

    public static Response Query<T1, T2, T3, T4, R1>(Func<T1, T2, T3, T4, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, ref R1 out1)
    {
        return QueryAll<T1, T2, T3, T4, object, object, R1>(method, in1, in2, in3, in4, null, null, ref out1);
    }

    public static Response Query<T1, T2, T3, T4, T5, R1>(Func<T1, T2, T3, T4, T5, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, ref R1 out1)
    {
        return QueryAll<T1, T2, T3, T4, T5, object, R1>(method, in1, in2, in3, in4, in5, null, ref out1);
    }

    public static Response Query<T1, T2, T3, T4, T5, T6, R1>(Func<T1, T2, T3, T4, T5, T6, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
    {
        return QueryAll<T1, T2, T3, T4, T5, T6, R1>(method, in1, in2, in3, in4, in5, in6, ref out1);
    }

    private static Response QueryAll<T1, T2, T3, T4, T5, T6, R1>(Delegate method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
    {
        try
        {
            Response response = null;

            // Test if the method's class implements ICacheable
            if (method.GetType() is ICacheable)
            {
                // Try to get the value from the cache if available
                out1 = ((ICacheable)method.Target).Get<R1>(out1);

                // If not null, return the value and exit
                if (out1 != null)
                    return null;
                else
                {
                    // Value is null, but should be cached, so attempt to load to cache and return it
                    if (in6 != null)
                        response = ((Func<T1, T2, T3, T4, T5, T6, R1>)method)(in1, in2, in3, in4, in5, in6, ref out1);
                    else if (in5 != null)
                        response = ((Func<T1, T2, T3, T4, T5, R1>)method)(in1, in2, in3, in4, in5, ref out1);
                    else if (in4 != null)
                        response = ((Func<T1, T2, T3, T4, R1>)method)(in1, in2, in3, in4, ref out1);
                    else if (in3 != null)
                        response = ((Func<T1, T2, T3, R1>)method)(in1, in2, in3, ref out1);
                    else if (in2 != null)
                        response = ((Func<T1, T2, R1>)method)(in1, in2, ref out1);
                    else if (in1 != null)
                        response = ((Func<T1, R1>)method)(in1, ref out1);
                    else
                        response = ((Func<R1>)method)(ref out1);

                    // If value from database is not null, save it in cache
                    if (out1 != null)
                        ((ICacheable)method.Target).Set<R1>(out1);

                    return response;
                }
            }
            else
            {
                // Get data from database
                if (in6 != null)
                    response = ((Func<T1, T2, T3, T4, T5, T6, R1>)method)(in1, in2, in3, in4, in5, in6, ref out1);
                else if (in5 != null)
                    response = ((Func<T1, T2, T3, T4, T5, R1>)method)(in1, in2, in3, in4, in5, ref out1);
                else if (in4 != null)
                    response = ((Func<T1, T2, T3, T4, R1>)method)(in1, in2, in3, in4, ref out1);
                else if (in3 != null)
                    response = ((Func<T1, T2, T3, R1>)method)(in1, in2, in3, ref out1);
                else if (in2 != null)
                    response = ((Func<T1, T2, R1>)method)(in1, in2, ref out1);
                else if (in1 != null)
                    response = ((Func<T1, R1>)method)(in1, ref out1);
                else
                    response = ((Func<R1>)method)(ref out1);

                return response;
            }
        }
        catch (Exception exc)
        {
            CustomException exception = exc.ToCustomException();
            exception.Code = ResponseCodes.UnknownError;
            throw exception;
        }
    }

This is the data abstraction layer. Again, my question is I want to allow a developer to pass in a method and up to 6 parameters. But, I only want one main method to contain all of my logic so that it's easier to maintain. Then, based on certain conditions (is the object in cache or not), call a method on the data layer to read in the object from the repository, store in cache, then return object to the controller.

Is there a better way to do this than multiple if/else statements like below?

Upvotes: 1

Views: 128

Answers (3)

a11smiles
a11smiles

Reputation: 1258

Thanks @ohmusama! I tweaked your code a bit because I needed to still use the returned reference parameter in my Generic query method, but you helped a ton!

Below is my final answer with one overloaded method:

    public static Response Query<T1, T2, T3, T4, T5, T6, R1>(Func<T1, T2, T3, T4, T5, T6, Tuple<Response, R1>> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
    {
        return QueryAll(() => method(in1, in2, in3, in4, in5, in6), ref out1);
    }

    private static Response QueryAll<R1>(Func<Tuple<Response, R1>> method, ref R1 out1)
    {
        try
        {
            Tuple<Response, R1> result;

            // Test if the method's class implements ICacheable
            if (method.GetType() is ICacheable)
            {
                // Try to get the value from the cache if available
                out1 = ((ICacheable)method.Target).Get<R1>(out1);

                // If not null, return the value and exit
                if (out1 != null)
                    return null;
                else
                {
                    // Value is null, but should be cached, so attempt to load to cache and return it
                    result = method();
                    out1 = result.Item2;

                    // If value from database is not null, save it in cache
                    if (out1 != null)
                        ((ICacheable)method.Target).Set<R1>(out1);

                    return result.Item1;
                }
            }
            else
            {
                // Get data from database
                result = method();
                out1 = result.Item2;

                return result.Item1;
            }
        }
        catch (Exception exc)
        {
            CustomException exception = exc.ToCustomException();
            exception.Code = ResponseCodes.UnknownError;
            throw exception;
        }
    }

Upvotes: 0

ohmusama
ohmusama

Reputation: 4215

You could have your business logic contained in another method, that has an argument for the method you want to call

public static Response Query<R1>(Func<Tuple<Result, R1>> method, ref R1 @out)
{
    Tuple<Result, R1> result = Logic(() => method());
    @out = result.Item2;
    return result.Item1;
}

public static Response Query<T1, R1>(Func<T1, Tuple<Result, R1>> method, T1 a, ref R1 @out)
{
    Tuple<Result, R1> result = Logic(() => method(a));
    @out = result.Item2;
    return result.Item1;
}

public static Response Query<T1, T2, R1>(Func<T1, T2, Tuple<Result, R1>> method, T1 a, T2 b, ref R1 @out)
{
    Tuple<Result, R1> result = Logic(() => method(a, b));
    @out = result.Item2;
    return result.Item1;
}

...

public static Tuple<Result, R1> Logic<R1>(Func<Tuple<Result, R1>> doMethod)
{
    Tuple<Result, R1> result;
    // logic
    if(true) { result = doMethod(); }
    ...

    // watch out if this doesn't get assigned, can cause problems downstream
    return result;
}

Upvotes: 2

Amandil
Amandil

Reputation: 473

What if instead putting T1, T2, and the others as separate parameters you would put them in a list? List<object>

You would get something similar to this:

public static Response Query<L1,R1>(L1 in1, ref R1 out1)
{
    // The SAME business logic with the same if/then statements
    if (true)
        out1 = method(in1, in2);
    else
        // Some other business logic
}

There would be no need to pass method as a parameter as it would always take the same list and the same number of parameters.

Then you'd perform the same operations in method by checking in1.Count and the type of each individual item: if (typeof(in1[0]) == T1) { ... }

Upvotes: 0

Related Questions