[英]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的集合。
我的問題是:
謝謝!
Skeet博士要求提供有關我的意圖的更多信息。
InputMapper類將用於將用戶輸入分配給任何對象的成員,並負責將用戶輸入轉換為屬性類型。 可以從上面的示例中推斷出該類的接口。
經過一番握手,喬恩和丹將我帶到了那里。 您可以提出改進建議嗎? 這是我所擁有的: 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.