Как передать параметр Generic Type в лямбда-выражение?

У меня есть лямбда-выражение, которое принимает int? (целое число, допускающее значение NULL),
которое возвращает значение, если оно существует, или DBNull.Value в противном случае.

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

Цель здесь в том, что я хочу сделать это выражение немного более общим, чтобы я мог передавать любые типы, допускающие значение NULL, например, DateTime?

Итак, вот нефункциональный код, с которого я начал, но не уверен, где указать тип nullable.

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

Можно ли указать общий тип или для этого нужно создать именованную функцию?


person dance2die    schedule 23.10.2009    source источник
comment
Невозможно создать общий анонимный метод, нет. Лучше всего это делать номинальным методом.   -  person Eric Lippert    schedule 24.10.2009


Ответы (3)


Поскольку целью вопроса является использование лямбда-выражения, вот решение. Он идет другим путем, используя слабую типизацию вместо предложенной строгой типизации, но, тем не менее, выполняет то же самое.

        // 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

Редактировать: эта свободная типизация работает, потому что тип данных лямбда-аргумента v относится к самой структуре, а не к оболочке типа Nullable. По-видимому, значение Nullable, которое использует вызывающий объект, было разрешено или «развернуто» к тому времени, когда оно попадает в аргумент лямбда, а аргумент лямбда показывает значение структуры или нуль; обертка Nullable нигде не видна на данный момент (или, насколько я могу найти). Это поведение можно проверить, поставив точку останова отладки в лямбда-выражение v и проверив его значение. Хорошим побочным эффектом такого поведения является то, что лямбда-выражение одинаково хорошо работает как для типов Nullable, так и для типов, не являющихся Nullable, — оно не ограничено.

person John K    schedule 23.10.2009
comment
Я пробовал это, и это работает, в чем был смысл моего вопроса. Но я отказался от преобразования лямбда в метод. - person dance2die; 24.10.2009
comment
Кстати, я впервые использую ReferenceEquals. Довольно освежающе использовать фактический метод Object. ;) - person dance2die; 24.10.2009

Вместо использования дженериков вы можете просто создать метод расширения для Object для выполнения преобразования.

Вот пример программы. Расширение ToDbObject выполняет преобразование:

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();
    } 
}

Вышеуказанные отпечатки:

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

Он правильно обрабатывает оба случая.

person Reed Copsey    schedule 23.10.2009

Я думаю, вы можете сделать это, создав фабричный метод делегата, в котором вы можете указать параметр универсального типа:

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

И вы можете использовать его в своем примере следующим образом:

SaveImage(
    CreateGetValueFunc<int>()(imageId),
    CreateGetValueFunc<DateTime>()(actionDate));
person Lee    schedule 23.10.2009
comment
Если он обернут в реальную функцию, разве это не спорный вопрос использования лямбды в первую очередь? Содержимое вашей функции также может быть не лямбда-кодом. - person John K; 24.10.2009
comment
@jdk прав со своим комментарием. Моя основная цель состояла в том, чтобы создать быстрое и грязное выражение, не прибегая к созданию новой функции (что не сложно, но хорошо), чтобы я мог узнать предел анонимных функций/лямбда-выражений. - person dance2die; 24.10.2009