วันเสาร์ที่ 5 เมษายน พ.ศ. 2551

Extension Methods for Delegate Types Part II - Passing anonymous type to generic function

How can you create function which its parameter or return type is anonymous type? You can't assign lambda expression to implicit local variable declaration.

//Compile error
var func = () => new { x = 1, y = 2 };

The c# compiler also does not accept type inference on instantiation, such as

//Compile error
var func = new Func(() => new { x = 1, y = 2 });

To create generic function, which accepts anonymous type, we must create method. Because for method, type inference is allowed.

Here is the code.

public static Func<TResult> CreateFunction<TResult>(Func<TResult> tResult) {
    return tResult;
}
public static Func<T, TResult> CreateFunction<T, TResult>(T t, Func<T, TResult> tResult) {
    return tResult;
}
public static Func<T1, T2, TResult> CreateFunction<T1, T2, TResult>(T1 t1, T2 t2, Func<T1, T2, TResult> tResult) {
    return tResult;
}
public static Func<T1, T2, T3, TResult> CreateFunction<T1, T2, T3, TResult>(T1 t1, T2 t2, T3 t3, Func<T1, T2, T3, TResult> tResult) {
    return tResult;
}
public static Func<T1, T2, T3, T4, TResult> CreateFunction<T1, T2, T3, T4, TResult>(T1 t1, T2 t2, T3 t3, T4 t4, Func<T1, T2, T3, T4, TResult> tResult) {
    return tResult;
}


To create the method just pass lambda expression to the argument.

var func = Function2.CreateFunction(() => new { x = 1, y = 2 });

You can also create function which its arguments and return type are anonymous type

var func = Function2.CreateFunction(
    new { Name = default(string), Age = default(int) },
    default(decimal),
    (person, salary) =>
        new {
            Name = person.Name,
            Age = person.Age,
            Salary = salary
        });
var p = func(new { Name = "Lisa", Age = 20 }, 1000000);
//p = { Name = Lisa, Age = 20, Salary = 1000000 }


Just pass the empty class to specify type of arguments. Above code is to create function which accept an anonymous type (Name: string, Age: int) and a decimal, the function returns another anonymous type (Name: string, Age: int, Salary: decimal).

As a bonus here are some more extension methods.

public static Func<T2, T3, TResult> Partial<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func, T1 arg1) {
    return (arg2, arg3) => func(arg1, arg2, arg3);
}
public static Func<T2, T3, T4, TResult> Partial<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> func, T1 arg1) {
    return (arg2, arg3, arg4) => func(arg1, arg2, arg3, arg4);
}

public static Func<T1, T2, T3, TResult> Memoize<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func) {
    var dict = new Dictionary<T1, Func<T2, T3, TResult>>();
    return (arg1, arg2, arg3) => {
        Func<T2, T3, TResult> value;
        if (!dict.TryGetValue(arg1, out value)) {
            value = func.Partial(arg1).Memoize();
            dict.Add(arg1, value);
        }
        return value(arg2, arg3);
    };
}
public static Func<T1, T2, T3, T4, TResult> Memoize<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> func) {
    var dict = new Dictionary<T1, Func<T2, T3, T4, TResult>>();
    return (arg1, arg2, arg3, arg4) => {
        Func<T2, T3, T4, TResult> value;
        if (!dict.TryGetValue(arg1, out value)) {
            value = func.Partial(arg1).Memoize();
            dict.Add(arg1, value);
        }
        return value(arg2, arg3, arg4);
    };
}

public static Func<T1, T2, T3, TResult> When<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func, Func<T1, T2, T3, bool> predicate, Func<T1, T2, T3, TResult> alternative) {
    return (arg1, arg2, arg3) => predicate(arg1, arg2, arg3) ? alternative(arg1, arg2, arg3) : func(arg1, arg2, arg3);
}
public static Func<T1, T2, T3, T4, TResult> When<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> func, Func<T1, T2, T3, T4, bool> predicate, Func<T1, T2, T3, T4, TResult> alternative) {
    return (arg1, arg2, arg3, arg4) => predicate(arg1, arg2, arg3, arg4) ? alternative(arg1, arg2, arg3, arg4) : func(arg1, arg2, arg3, arg4);
}


These are Parial, Memoize, and When functions, which are the same as previous post. You can find detail of functions in previous post. The only difference of these functions and functions in previous post is, these functions accept 3 and 4 arguments.

Next post, extension methods for math.

ไม่มีความคิดเห็น: