оператор индексированного переключателя или эквивалент?.net, C # - PullRequest
6 голосов
/ 29 июля 2010

Я хочу создать метод, который принимает строковый параметр, и объект, который я хотел бы вернуть конкретному члену на основе этого параметра.Итак, самый простой способ - это создать оператор 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: Я ищу способ объединить производительность индексированного / хешированного поиска (как в словаре) с возвратом определенных свойств переданного объекта.Я, скорее всего, помещу это в статический метод внутри каждого класса, для которого он используется.

Ответы [ 7 ]

8 голосов
/ 29 июля 2010

Вот быстрый макет чего-то, что могло бы работать для любого класса (используя отражение, а не оператор 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.");
}

Очевидно, что вам нужно выполнить дополнительную проверку ошибок, но это было бы началом.

Я также возвратил объект типа из методов по причине. Это позволяет вызывающей стороне обрабатывать приведение значения так, как оно считает нужным (если ему вообще необходимо выполнить приведение).

7 голосов
/ 29 июля 2010

Вот простой способ использования словаря:

    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;
    }
5 голосов
/ 29 июля 2010

Существует несколько опций, которые вы можете попробовать.

Опция 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, когда операторы регистра чисел достигнут определенного порога.Этот порог равен 7 на компиляторе C # 3.0.Таким образом, поиск будет O (1), независимо от того, сколько существует операторов case.Он не будет сканировать каждый из них.

2 голосов
/ 29 июля 2010

Вы можете использовать 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);
    }
}
0 голосов
/ 29 июля 2010

Вы можете попробовать использовать что-то вроде этого.

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);
    }
}
0 голосов
/ 29 июля 2010

Вы не можете сделать это с индексом, но вы можете использовать отражение.

0 голосов
/ 29 июля 2010

Что ж, если предположить, что Name соответствует фактическому имени свойства (в отличие от вашего примера), это, вероятно, лучше всего обработать с помощью отражения.

...