2016-10-13 37 views
0

我正在構建一個幫助函數來將對象的屬性映射到對象,該對象知道屬性綁定到哪個UI組件以及如何來回轉換它們的值。如何在c#中使用通用轉換器的映射

現在我需要映射本身來映射屬性名稱(String類型)到它們的ModelFieldItems,它們包含一個通用轉換器(例如StringToBoolConverter)。但由於缺乏<?>運算符,我習慣於從java中,我不能簡單地寫private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>(); 其中ModelFieldItem包含類型信息和轉換器本身。

因此,我需要添加特定的類型,如果我想添加一個ModelFieldItem到地圖,但它們的類型不同。 我已經嘗試使用dynamicobject作爲類型參數,但遲早我達到了一個點,它不起作用。

使用的伎倆從C# - Multiple generic types in one list我得到了編譯器高興,但我需要一個類型轉換來訪問我的轉換邏輯,這使我在下列條件

if (item is ModelFieldItem<dynamic, dynamic>) 
{ 
    ModelFieldItem<dynamic, dynamic> dynamicItem = (ModelFieldItem<dynamic, dynamic>)item; 

總是解析爲false

編輯

的方法,這將啓動轉換,不知道,這類型的轉換,這是需要的。我們使用一個循環遍歷這些屬性並啓動它們相應的轉換器。所以在轉換點上進行轉換不能用不同的類型來完成,因爲我們無法知道它們。

我們的轉換器是那麼容易,因爲他們可以從以下abstract class

public abstract class TypedConverter<A, B> 
{ 
    public abstract B convertFrom(A value); 

    public abstract A convertTo(B value); 
} 

繼承正如前面提到的,我從Java的背景是,所以我想達到的目標大體類似在Java中

以下
private Map<String, ModelFieldItem<?,?>> converters = new HashMap<>(); 

,我會用它大致是

converters.put("key", new Converter<String, Boolean>()); 

如何在C#中實現此目的?

+0

您是否正在使用反射來設置其他模型的屬性(通過'SetValue')?反射通常與'對象'一起工作,所以類型安全不會購買任何東西。也就是說,你可以使用一個非泛型的'ModelFieldItem'和一個底層的'Func '委託。 –

+0

此外,請查看'Convert.ChangeType'作爲爲每個屬性指定轉換器的替代方法 - 或者至少作爲默認實現。您可能還想查看Automapper,它專門用於映射屬性。 –

+0

@TravisParks:感謝您的意見。我們需要我們的轉換器中的類型安全性:a)使實現更加清晰,b)在模型上設置正確的類型,而通過反思,我們仍然需要設置正確的類型,還是我錯誤了?使用委託的想法很好,我會嘗試。 – Phe0nix

回答

1

通常當你遇到這種類型的問題時,你必須使用你的類的抽象,不需要泛型類型。

如果您的ModelFieldItem實現了一個沒有泛型參數的接口,您可以使用它。

var _dict = new Dictionary<String, IModelFieldItem>() 
{ 
    new Converter<string, bool>() // If it's implement IModelFieldItem 
}; 

(YouWillHaveToCastUnlessYouUseDynamicType)_dict[key].Convert("true"); 

否則,另一種方法是通過objectdynamic,以取代從Dictionary<String, ModelFieldItem>ModelFieldItem,那麼你可以訪問從你的字典中的值時,將其丟。

var _dict = new Dictionary<String, object>() 
{ 
    new Converter<string, bool>() 
}; 

// You can avoid the cast too by using dynamic 
// But it will cost you some perf 
((Converter<string, bool>)_dict[key]).Convert("true"); 

如果你知道你想要的類型。
你可以做這樣的事情:

var _dict = new Dictionary<String, object>() 
{ 
    new Converter<string, bool>() 
}; 

public void Convert<TToConvert, TConverted>(string key, TToConvert valueToConvert, out TConverted valueConverted) 
{ 
    valueConverted = (T)_dict[key].Convert(valueToConvert); 
} 

bool value; 
Convert("Key", "true", out value); 

這裏你可以做一個其他例子:

public static void Convert<TToConvert, TConverted>(TToConvert valueToConvert, out TConverted valueConverted) 
    { 
     // You should put the dictionary outside of the method 
     // To avoid to instance it, each time you call this method 
     var dict = new Dictionary<Type, Func<object, object>>() 
     { 
      { typeof(Tuple<string, int>), x => int.Parse((string)x) }, 
      { typeof(Tuple<string, bool>), x => bool.Parse((string)x) } 
     }; 
     valueConverted = (TConverted)dict[typeof(Tuple<TToConvert, TConverted>)](valueToConvert); 
    } 

    static void Main(string[] args) 
    { 
     bool boolTest; 
     Convert("false", out boolTest); 
     Console.WriteLine(boolTest); 
     int intTest; 
     Convert("42", out intTest); 
     Console.WriteLine(intTest); 
     Console.ReadKey(); 
    } 

Obvisouly,你應該嘗試,如果你可以將你的類型,第一和也如果轉換成功。最後讓Convert返回一個布爾值來知道它是否成功。
但至少可以看到,進行轉換時不再需要string key,它可能會讓您感興趣。 您還必須確保當您將它們傳遞給方法時,您的變量具有正確的類型,否則您將搜索錯誤的鍵。


思考解決方案:

通過上述方法,你可以做這樣的事情:

static void Main(string[] args) 
{ 
    object[] parameters = new object[] { "false", true }; 
    typeof(Program).GetMethod("Convert") 
    // Be sure that the types will create a valid key 
     .MakeGenericMethod(new Type[] { parameters[0].GetType(), parameters[1].GetType() }) 
    // Change null to your instance 
    // if you are not calling a static method 
     .Invoke(null, parameters); 
    // parameters[1] is an out parameter 
    // then you can get its value like that 
    Console.WriteLine(parameters[1]); 
    Console.ReadKey(); 
} 

具有屬性,看起來應該像這樣的:

object[] parameters = new object[] 
{ 
    propertyToRead.GetValue(objectToRead), 
    propertyToSet.GetValue(objectToSet) 
}; 

typeof(MapperObject).GetMethod("Convert") 
    .MakeGenericMethod(new Type[] 
     { 
      propertyToRead.PropertyType, 
      propertyToSet.PropertyType 
     }) 
    .Invoke(mapperInstance, parameters); 

propertyToSet.SetValue(objectToSet, parameters[1]); 

你可能需要稍微調整一下,因爲我沒有試圖編譯它


我能得到的解決方案,但我也不知道什麼是你的產業架構也沒怎麼你的Converter作品。

+0

使用反射只要記住'dynamic'具有*巨大*性能影響。 – Bauss

+0

是的,我知道。我也避免他們。但無論如何,它也可能回答這個問題。 –

+0

@Bauss,這將是很好的,如果下一次,你可以至少讓我們完成寫下我們的職位之前downvoting它;) –