簡體   English   中英

C#Lambda表達式+反射問題

[英]c# lambda expression + reflection questions

這主要是出於教育目的。 我正在嘗試創建本示例中使用的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()將遍歷所有映射,以嘗試轉換用戶輸入並將其分配給lambda表達式中使用的屬性。 隨着循環的進行,它將保存拋出的所有FormatException的集合。

我的問題是:

  • 在InputMapper.Map()方法中,lambda參數類型應該是什么?
  • 在InputMapper.CreateInstance()方法中,如何嘗試在已創建的T實例上設置屬性?

謝謝!

更新資料

Skeet博士要求提供有關我的意圖的更多信息。

InputMapper類將用於將用戶輸入分配給任何對象的成員,並負責將用戶輸入轉換為屬性類型。 可以從上面的示例中推斷出該類的接口。

更新2

經過一番握手,喬恩和丹將我帶到了那里。 您可以提出改進建議嗎? 這是我所擁有的: http : //pastebin.com/RaYG5n2h

對於第一個問題, Map方法可能應該是通用的。 例如:

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

現在,值得注意的是,您的lambda表達式表示屬性獲取 ,但想必你要撥打的屬性設置器 這意味着您的代碼將不是編譯時安全的,例如,可能存在一個只讀的映射屬性。 而且,沒有什么可以限制lambda表達式引用屬性。 它可以做任何事情。 您必須在執行時采取防范措施。

一旦找到了物業制定者,雖然,你只需要創建一個實例TSource使用new TSource()注意,構造函數約束TSource以上),然后進行適當的轉換和調用屬性setter。

如果沒有更多關於您要執行的操作的詳細信息,恐怕要給出更詳細的答案並不容易。

編輯:算出該屬性的代碼將如下所示:

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 :看起來您在我發布此消息之前不久就已經弄清楚了。好!無論如何,我都會把它留在那里作為參考,即使它不是非常有效的實現。)

下面是一個演示程序。

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簡單方法。)

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中的表達式不是屬性引用,則可以添加一些檢查以引發更好的異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM