Получение свойств, определенных в анонимном типе, отправленном через Func

#c# #reflection #anonymous-types

#c# #отражение #анонимные типы

Вопрос:

Допустим, у нас есть следующие классы, определенные следующим образом

 class Test {
    public string Name { get; set; }
    public string Type { get; set; }
    public string SomeOtherValue { get; set; }
    public Address Address { get; set; }
}

class Address {
    public string AddressText { get; set; }
    public string PostalCode { get; set; }
}
 

Затем мне нужно внутри метода что-то сделать с одним или несколькими свойствами в этом классе, и я хотел бы определить, какие именно, безопасным для компиляции способом. Итак, я хочу вызвать метод следующим образом:

 MyFunction<Test>(t => new {t.Name, t.Address.AddressText});
 

Затем внутри моей функции мне нужно знать это «Имя» и «Адрес».AddressText» — это выбранные свойства, но я не уверен, как их получить

 public void MyFunction<T>(Func<T, ?> param){
    ...
}
 

Итак, моя проблема в том, что я не могу установить? как TResult Func в любом хорошем смысле. Если бы я мог правильно это настроить, я думаю, я мог бы взять typeOf(TResult).GetProperties() , но я чувствую, что мне не хватает части, чтобы заставить это работать.

Обновить

Я вижу, что есть путаница в том, что я пытаюсь здесь сделать. Я приведу пример из драйвера MongoDB, который ведет себя так же, как я хотел бы, чтобы мой код делал

Итак, допустим, testcoll это коллекция Test объектов. В этом случае это сработает и вернет мне только список анонимного типа, который имеет только свойство Name .

 await testcoll.Find(t => t.Type == "sometype").Project(t => new {t.Name}).ToListAsync();
 

Итак, вопрос в основном. Внутри Project , как я могу определить, что Name это те свойства, которые я хочу извлечь из базы данных, и вернуть в результате анонимный тип?

Комментарии:

1. Извините, просто опечатка. Так привыкли писать prop для автозаполнения свойств в IDE 😉

2. Почему вы не можете создать класс / структуру или даже кортеж? Может ли вызывающий объект выбирать другие свойства?

3. Да, это связано с проекцией для поиска. Это должно указывать, какие поля я заинтересован в возврате, и будет отличаться каждый раз, когда я вызываю myFunction

4.Тогда вы MyFunction не можете точно знать, что свойства Name и AddressText существуют, верно? Возможно, вы не «заинтересованы в этих областях». MyFunction невозможно сделать это с безопасностью типов во время компиляции, поскольку не существует вариационных обобщений, если только вас не интересует постоянное количество полей каждый раз.

5. Говоря о безопасности типов компиляции, я имел в виду вызов функции. Я хочу, чтобы параметр был новым {t.Name , т.е.Адрес. AddressText}, а не массив магических строк, подобных этому new []{ «Имя», «Адрес. AddressText»}. Но я понимаю, что получение отправленных фактических значений должно быть выполнено во время выполнения, и это совершенно нормально.

Ответ №1:

Хорошо, теперь я решил эту проблему. Возможно, проблема в том, что мое объяснение в вопросах и есть проблема. Я хочу получить безопасность во время компиляции для параметра, а не получать фактическое значение времени компиляции. Это, конечно, не сработало бы.

Поэтому я хочу иметь возможность писать

 MyFunction<Test>(t => new {t.Name, t.Address.AddressText});
 

Вместо

 MyFunction<Test>(new {"Name", "Address.AddressText"});
 

Так что я не получу никаких опечаток в отправленных параметрах и удостоверюсь, что любое изменение тестового объекта приведет к ошибке во время компиляции.

Я решил это так:

 public void MyFunction<T>(Expression<Func<T, object>> lambda)
{
    List<string> arguments;
    if (lambda.Body is NewExpression)
    {
        var args = (lambda.Body as NewExpression).Arguments;
        arguments = args.Select(a =>
        {
                var aText = a.ToString();
                var dotIndex = aText.IndexOf(".");
                var result = aText.Substring(dotIndex   1);
                return resu<
            }).ToList();
        }
        else
        {
            throw new ArgumentException();
        }
    }
    <do whatever I need to do here with the arguments list>
}
 

Итак, для примера выше, где аргумент t => new {t.Name, t.Address.AddressText} — это, который будет преобразован в список со строками "Name" и "Address.AddressText" внутри метода.