c # лямбда-выражение + вопросы для размышления - PullRequest
1 голос
/ 21 сентября 2010

Это в основном для образовательных целей.Я пытаюсь создать класс InputMapper, который используется в этом примере:

var mapper = new InputMapper<SomeType>();
mapper.Map("some user input", someType => someType.IntProperty, "Input was not an integer");
mapper.Map("some user input", someType => someType.BoolProperty, "Input was not a bool");

SomeType someTypeInstance = mapper.CreateInstance();

Мой класс InputMapper содержит коллекцию всех отображений, созданных с помощью метода Map ().CreateInstance () будет перебирать отображения, пытаясь преобразовать введенные пользователем данные и назначать их свойству, используемому в лямбда-выражении.При прохождении цикла он сохранит коллекцию любых брошенных исключений FormatException.

Мои вопросы:

  • В методе InputMapper.Map (), что должен иметь параметр lambdatype be?
  • В методе InputMapper.CreateInstance () как мне попытаться установить свойства созданного мной экземпляра T?

Спасибо!

Обновление

Др.Skeet запросил дополнительную информацию о моих намерениях.

Класс InputMapper будет использоваться для назначения пользовательского ввода членам любого объекта, заботясь о преобразовании пользовательского ввода в тип свойств.Интерфейс класса может быть выведен из приведенного выше примера.

Обновление 2

После некоторого удержания руки Джон и Дэн привели меня туда.Можете ли вы предложить улучшения?Вот что у меня есть: http://pastebin.com/RaYG5n2h

Ответы [ 3 ]

5 голосов
/ 21 сентября 2010

Для вашего первого вопроса метод Map, вероятно, должен быть универсальным. Например:

public class InputMapper<TSource> where TSource : new()
{
    public void Map<TResult>(string input,
                             Expression<Func<TSource, TResult>> projection,
                             string text)
    {
        ...
    }
}

Теперь стоит отметить, что ваши лямбда-выражения представляют свойство getters , но, вероятно, вы хотите вызвать свойство setters . Это означает, что ваш код не будет безопасным во время компиляции - может быть сопоставленное свойство, которое доступно только для чтения, например. Кроме того, нет ничего, чтобы ограничить лямбда-выражение только ссылкой на свойство. Это может сделать что угодно. Вы должны были бы поставить охрану времени исполнения против этого.

После того как вы нашли найденные установщики свойств, вам просто нужно создать экземпляр TSource, используя new TSource() (обратите внимание на ограничение конструктора на TSource выше), а затем выполнить соответствующие преобразования и позвоните установщикам собственности.

Без более подробной информации о том, что вы пытаетесь сделать, боюсь, не очень просто дать более подробный ответ.

РЕДАКТИРОВАТЬ: код для обработки свойства будет выглядеть примерно так:

var memberExpression = projection.Body as MemberExpression;
if (memberExpression == null)
{
    throw new ArgumentException("Lambda was not a member access");
}
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
{
    throw new ArgumentException("Lambda was not a property access");
}
if (projection.Parameters.Count != 1 ||
    projection.Parameters[0] != memberExpression.Expression)
{
    throw new ArgumentException("Property was not invoked on parameter");
}
if (!propertyInfo.CanWrite)
{
    throw new ArgumentException("Property is read-only");
}
// Now we've got a PropertyInfo which we should be able to write to - 
// although the setter may be private. (Add more tests for that...)
// Stash propertyInfo appropriately, and use PropertyInfo.SetValue when you 
// need to.
1 голос
/ 21 сентября 2010
public void Map<TValue>(string input, Expression<Func<T, TValue>> property, string message);

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

public T CreateInstance() 
{
    T result = new T();
    foreach (var lambda in map) 
    {
        ((PropertyInfo)((MemberExpression)lambda.Body).Member).SetValue(result, null, propertyValueForLambdaThatYouStored);  
    }
    return result;
}

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

1 голос
/ 21 сентября 2010

Обновление : см. здесь , чтобы получить рабочий пример того, что, я думаю, вы пытаетесь сделать.

( Обновление 2 : Похоже, вы уже поняли это незадолго до того, как я опубликовал это. Хорошо! Я все равно оставлю это для справки, хотя это не очень эффективная реализация.)

Нижедемонстрационная программа.

class CustomType
{
    public int Integer { get; set; }
    public double Double { get; set; }
    public bool Boolean { get; set; }
    public string String { get; set; }

    public override string ToString()
    {
        return string.Format("int: {0}, double: {1}, bool: {2}, string: {3}", Integer, Double, Boolean, String);
    }
}

class Program
{
    public static void Main(string[] args)
    {
        var mapper = new InputMapper<CustomType>();

        mapper.Map("10", x => x.Integer, "Unable to set Integer property.");
        mapper.Map("32.5", x => x.Double, "Unabled to set Double property.");
        mapper.Map("True", x => x.Boolean, "Unable to set Boolean property.");
        mapper.Map("Hello world!", x => x.String, "Unable to set String property.");

        var customObject = mapper.Create();

        Console.WriteLine(customObject);

        Console.ReadKey();
    }
}

Вывод:

int: 10, double: 32.5, bool: True, string: Hello world!

Похоже, вы хотите определить свою Map функцию следующим образом:

class InputMapper<T>
{
    public void Map<TProperty>(string input,
                               Expression<Func<T, TProperty>> propertyExpression,
                               string errorMessage);
}

Тогда, по-видимому, вы хотите из propertyExpression определить, какое свойство вы хотите установить на основе пользовательского ввода.Это верно?

Я не полностью ясно, почему вы не хотите просто определить это так:

class InputMapper<T>
{
    public void Map<TProperty>(string input,
                               Action<TProperty> propertySetter,
                               string errorMessage);
}

Тогда использованиебудет выглядеть примерно так:

mapper.Map<int>(
    "some user input",
    value => someType.IntProperty = value,
    "Input was not an integer"
);

(обратите внимание, что ваша функция Map<TProperty> внутренне должна была бы обрабатывать вопрос парсинга пользовательского ввода в соответствующий тип, возможно, используя что-то простое, например Convert.ChangeType.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...