Индексированный оператор switch или эквивалент?.net, С#
Я хочу создать метод, который принимает параметр string, и объект, который я хотел бы вернуть определенному члену на основе param. Итак, самый простой способ - создать оператор switch:
public GetMemberByName(MyObject myobj, string name)
{
switch(name){
case "PropOne": return myobj.prop1;
case "PropTwo": return myobj.prop2;
}
}
Это отлично работает, но я могу завершить довольно большой список... Поэтому мне было любопытно, если бы был способ, не создавая кучу вложенных структур if-else, чтобы выполнить это индексированным образом, чтобы поле сопоставления определяется индексом вместо перехода через переключатель до тех пор, пока не будет найдено совпадение.
Я рассмотрел использование Dictionary<string, something>
, чтобы обеспечить быстрый доступ к сопоставимым строкам (как к элементу ключа), но поскольку я хочу получить доступ к члену переданного объекта, я не уверен, как это может быть сделано.
-
Я стараюсь избегать отражения и т.д., чтобы иметь очень быструю реализацию. Я, скорее всего, буду использовать генерацию кода, поэтому решение не должно быть маленьким/жестким и т.д.
-
Я изначально строил словарь, но каждый объект инициализировал его. Поэтому я начал переводить это на один метод, который может искать значения на основе ключей - оператор switch. Но поскольку я больше не индексирован, я боюсь, что непрерывный поиск, вызывающий этот метод, будет медленным.
-
SO: Я ищу способ комбинировать производительность индексированного/хешированного поиска (например, использование словаря) с возвратом определенных свойств передаваемого объекта. Скорее всего, это статический метод в каждом классе, для которого он используется.
Ответы
Ответ 1
Здесь вы можете использовать словарь:
Dictionary<string, Func<MyObject, object>> propertyNameAssociations;
private void BuildPropertyNameAssociations()
{
propertyNameAssociations = new Dictionary<string, Func<MyObject, object>>();
propertyNameAssociations.Add("PropOne", x => x.prop1);
propertyNameAssociations.Add("PropTwo", x => x.prop2);
}
public object GetMemberByName(MyObject myobj, string name)
{
if (propertyNameAssociations.Contains(name))
return propertyNameAssociations[name](myobj);
else
return null;
}
Ответ 2
Вот быстрый макет того, что может работать для любого класса (используя отражение, а не оператор switch):
public static object GetMemberByName<T>(T obj, string name)
{
PropertyInfo prop = typeof(T).GetProperty(name);
if(prop != null)
return prop.GetValue(obj, null);
throw new ArgumentException("Named property doesn't exist.");
}
Или версия метода расширения (которая все равно будет работать над любым типом объекта):
public static object GetMemberByName<T>(this T obj, string name)
{
PropertyInfo prop = typeof(T).GetProperty(name);
if(prop != null)
return prop.GetValue(obj, null);
throw new ArgumentException("Named property doesn't exist.");
}
Очевидно, есть дополнительная проверка ошибок, которую вы, возможно, захотите сделать, но это будет начало.
Я также возвратил объект типа из методов по какой-либо причине. Это позволяет вызывающему лицу обрабатывать значение, но, по-видимому, оно подходит (если им вообще нужно отбрасывать).
Ответ 3
Есть несколько вариантов, которые вы можете попробовать.
Вариант 1: динамический объект хранит значения свойств.
public GetMemberByName(MyObject myobj, string name)
{
return myobj.GetProperty(name);
}
public class MyObject
{
private Dictionary<string, object> m_Properties = new Dictionary<string, object>();
public object GetProperty(string name)
{
return m_Properties[name];
}
public void SetProperty(string name, object value)
{
m_Properties[name] = value;
}
public object Prop1
{
get { return GetProperty("PropOne"); }
set { SetProperty("PropOne", value); }
}
public object Prop2
{
get { return GetProperty("PropTwo"); }
set { SetProperty("PropTwo", value); }
}
}
Вариант 2: используйте отражение.
public GetMemberByName(MyObject myobj, string name)
{
return typeof(MyObject).GetProperty(name).GetValue(obj, null);
}
Вариант 3. Оставьте его таким, как есть.
Это разумный вариант, потому что операторы switch в строковых типах данных будут преобразованы в поиск Dictionary
, как только аргументы case case достигнут определенного порогового значения. Этот порог равен 7 в компиляторе С# 3.0. Таким образом, поиск будет равен O (1), независимо от того, сколько из них имеет место. Он не будет сканировать каждый из них.
Ответ 4
Вы можете использовать reflection
, чтобы динамически получить свойство во время выполнения. Вот фрагмент из небольшой утилиты, которую я написал. Это написано как метод расширения, который легко позволит вам получить свойство из вашего экземпляра класса
myInstance.GetProperty<string>("Title"); // Replace 'string' with the proper return value.
Код:
public static class ReflectionExtensions
{
private const BindingFlags DefaultFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
public static T GetProperty<T>(this object instance, string propertyName)
{
PropertyInfo property = GetPropertyInfo(instance, propertyName);
if (property == null)
{
var message = string.Format("The Type, '{0}' does not implement a '{1}' property", instance.GetType().AssemblyQualifiedName, propertyName);
throw new NotImplementedException(message);
}
return (T)property.GetValue(instance, null);
}
private static PropertyInfo GetPropertyInfo(object instance, string propertyName)
{
Type type = instance.GetType();
return type.GetProperty(propertyName, DefaultFlags);
}
}
Ответ 5
Ну, если предположить, что Имя соответствует фактическому имени свойства (в отличие от вашего примера), это, вероятно, лучше всего обрабатывать с помощью отражения.
Ответ 6
Вы не можете сделать это с индексом, но вы можете использовать отражение.
Ответ 7
Возможно, вы захотите попробовать что-то вроде этого.
private static readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _cache = new Dictionary<Type,Dictionary<string,PropertyInfo>>();
public static T GetProperty<T>(object obj, string name)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
else if (name == null)
{
throw new ArgumentNullException("name");
}
lock (_cache)
{
var type = obj.GetType();
var props = default(Dictionary<string, PropertyInfo>);
if (!_cache.TryGetValue(type, out props))
{
props = new Dictionary<string, PropertyInfo>();
_cache.Add(type, props);
}
var prop = default(PropertyInfo);
if (!props.TryGetValue(name, out prop))
{
prop = type.GetProperty(name);
if (prop == null)
{
throw new MissingMemberException(name);
}
props.Add(name, prop);
}
return (T)prop.GetValue(obj, null);
}
}