How to pass Generic Type parameter to lambda expre

2019-07-02 22:24发布

问题:

I have a lambda expression which accepts, a int? (nullable integer),
which returns value if value exists or DBNull.Value otherwise.

Func<int?, object> getId = id => id.HasValue ? id.Value : (object)DBNull.Value;

The goal here is that, I want to make that expression slightly a bit more generic so that I can pass any nullable types like, DateTime?

So here is a non-functional code I was starting off with, but not sure where to specify nullable's type.

int? imageId;
DateTime? actionDate;
Func<Nullable<T>, object> getValue = 
    id => id.HasValue ? id.Value : (object) DBNull.Value;
SaveImage(getValue(imageId), getValue(actionDate));

Is it possible to specify generic type or should I create a named function to do so?

回答1:

Since the purpose of the question is to use a lambda expression, here is a solution. It takes a different route by using weak typing instead of the proposed strong typing, but accomplishes the same thing nonetheless.

        // A lambda solution
        Func<object, object> fnGetValue =
            v =>
                ReferenceEquals(v, null)
                ? DBNull.Value
                : v;


        // Sample usage
        int? one = 1;
        int? two = null;
        object o1 = fnGetValue(one); // gets 1
        object o2 = fnGetValue(two); // gets DBNull

Edit: This loose typing works because the data type of the lambda argument v is of the struct itself and is not the Nullable type wrapper. Apparently the Nullable value that the caller uses has been resolved or 'unwrapped' by the time it hits the lambda argument and the lambda argument reveals a struct value or null; the Nullable wrapper is nowhere to be seen at this point (or as far as I can find). This behaviour can be proved by putting a debug breakpoint in the lambda at v and inspecting its value. The good side effect of this behaviour is the lambda works equally well for both Nullable and non-Nullable types -- it's not restricted.



回答2:

Instead of using generics, you can just make an extension method on Object to do the conversion.

Here's a sample program. The ToDbObject extension does the conversion:

using System;

static class Program
{
    static object ToDbObject(this object value)
    {
        return value ?? DBNull.Value;
    }
    static void Main(string[] args)
    {
        int? imageId = 3; 
        DateTime? actionDate = null;

        Console.WriteLine("ImageId {0}: [{1}] - {2}", imageId, imageId.ToDbObject(), imageId.ToDbObject().GetType());
        Console.WriteLine("actionDate {0}: [{1}] - {2}", actionDate, actionDate.ToDbObject(), actionDate.ToDbObject().GetType());
        Console.ReadKey();
    } 
}

The above prints:

ImageId 3: [3] - System.Int32
actionDate : [] - System.DBNull

It's correctly handling both cases.



回答3:

I think you can do it by creating a delegate factory method where you can specify the generic type parameter:

public static Func<Nullable<T>, object> CreateGetValueFunc<T>() where T : struct
{
    return id => id.HasValue ? id.Value : (object)DBNull.Value;
}

And you can use it in your example like this:

SaveImage(
    CreateGetValueFunc<int>()(imageId),
    CreateGetValueFunc<DateTime>()(actionDate));