代码之家  ›  专栏  ›  技术社区  ›  Ronnie Overby

c#lambda表达式+反思问题

  •  1
  • Ronnie Overby  · 技术社区  · 14 年前

    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()将遍历试图转换用户输入的映射,并将其分配给lambda表达式中使用的属性。当它循环通过时,它将保存抛出的所有formatexception的集合。

    我的问题是:

    • 在输入映射器.Map()方法,lambda参数类型应该是什么?

    谢谢!

    更新

    斯基特医生问我的意图。

    InputMapper类将用于将用户输入分配给任何对象的成员,并负责将用户输入转换为properties类型。类的接口可以从上面的示例中推断出来。

    更新2

    经过一番牵手,乔恩和丹把我带到了那里。你能提出改进建议吗?以下是我所拥有的: http://pastebin.com/RaYG5n2h

    3 回复  |  直到 4 年前
        1
  •  5
  •   Jon Skeet    14 年前

    对于你的第一个问题 Map

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

    现在值得注意的是,lambda表达式表示属性 ,但大概你想给酒店打电话 设置器 只有

    一旦你 TSource 使用 new 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.
    
        2
  •  1
  •   Ronnie Overby    14 年前
    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;
    }
    

    如果lambda中的表达式不是属性引用,则可以添加一些检查以引发更好的异常。

        3
  •  1
  •   Dan Tao    14 年前

    :参见 here

    ( :看起来在我发布此消息之前不久,您就已经知道了。很好!不管怎样,我将把它放在那里作为参考,尽管它不是一个非常有效的实现。)

    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 .)