Индексированный оператор 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);
    }
}