2016-10-13 4 views
0

Я создаю вспомогательную функцию для сопоставления свойств объекта с объектом, который знает, к какому компоненту пользовательского интерфейса привязан свойство и как преобразовать его значения взад и вперед.Как использовать карту общих преобразователей в C#

Теперь мне нужна сама карта, чтобы отобразить имена свойств (типа String) в их ModelFieldItems, которые содержат общий конвертер (например, StringToBoolConverter). Но из-за отсутствия оператора <?> я привык к Java, я не могу просто написать private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>(); , где ModelFieldItem содержит информацию о типе и самом преобразователе.

В результате мне нужно добавить определенные типы, если я хочу добавить ModelFieldItem к карте, но они различаются по типу. Я уже пытался использовать dynamic или object как параметры типа, но рано или поздно я дойду до точки, где она не работает.

Используя трюк из C# - Multiple generic types in one list я получил компилятор счастливым, но мне нужен был приведение типа доступ к моему логику преобразования, которые привели меня к следующему условию

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

Который всегда решает для false.

EDIT

Метод, который запускает преобразование, не знает, какие типы для преобразования, которые желательно. Мы используем цикл для итерации по свойствам и запуска их соответствующих преобразователей. Таким образом, кастинг в точке преобразования не может быть выполнен с помощью разных типов, потому что мы не можем их знать.

Наши преобразователи так же легко, как они могут быть, наследуя из следующего 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)? Отражение обычно работает с 'object', поэтому безопасность типа не покупает ничего. Тем не менее, вы можете использовать не-общий «ModelFieldItem» с базовым 'Func ' delegate. –

+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"); 

В противном случае, другой путь был бы заменить ModelFieldItem от Dictionary<String, ModelFieldItem> по object или dynamic, то вы можете привести его при обращении к значению из словаря.

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, необходимых для совершения конверсии, и это может вас заинтересовать. Вы также должны быть уверены, что ваши переменные имеют правильный тип, когда вы передаете их методу, иначе вы будете искать неправильный ключ.


Reflection решение:

С описанным выше способом вы можете сделать что-то подобное:

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]); 

You может потребоваться немного изменить его, так как я не пытался его скомпилировать


Я не мог дать другое решение, но я не знаю, что ваше наследство архитектура, ни как ваши Converter работы.

+0

Просто помните, что динамическое значение имеет * HUGE *. – Bauss

+0

Да, я знаю. Я тоже их избегаю. Но в любом случае, он может ответить на проблему. –

+0

@Bauss, было бы неплохо, если бы в следующий раз вы могли, по крайней мере, позволить нам закончить запись нашего сообщения целиком, прежде чем его downvoting;) –

Смежные вопросы