【问题标题】:indexed switch statement, or equivalent? .net, C#索引 switch 语句,或等效的? .net, C#
【发布时间】:2010-07-29 18:10:58
【问题描述】:

我想构建一个接受字符串参数的方法,以及一个我想根据参数返回特定成员的对象。因此,最简单的方法是构建一个 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:我正在寻找一种将索引/散列查找的性能(如字典使用)与返回传入对象的特定属性相结合的方法。我可能会将它放在它用于的每个类中的静态方法中。

【问题讨论】:

  • 由于 myobj 似乎非常具体(而不是仅仅 object),myobj.Property 还不够,而不是 GetMemberByName(myobj, "Property")?
  • 只是一个想法,根据标题,我完全误解了您的意图。该示例为我清除了它。看起来这个问题更像是“如何通过名称访问对象的属性”,对吧?
  • 为什么name 是一个字符串?这是用户输入的东西吗?它来自文件吗?有什么原因它不是enum
  • 我现在在问题中添加一些 cmets,谢谢

标签: c# dictionary switch-statement


【解决方案1】:

这是一个适用于任何类的快速模型(使用反射而不是 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.");
}

显然,您可能还想做一些额外的错误检查,但这只是一个开始。

出于某种原因,我还从方法中返回了类型对象。这允许调用者处理他们认为合适的转换值(如果他们需要转换的话)。

【讨论】:

  • 有趣的是,Wallace Breza 的版本接受一个对象并返回一个 T,而这个版本接受一个 T 并返回一个对象。您还可以有一个对输入和输出都通用的方法。
  • @John M Gant:我选择在返回对象上使其通用,因此在使用返回值时不涉及任何转换。我还希望扩展方法显示在所有对象的智能中。
  • @Wallace,它仍然适用于所有对象。
  • @John M Gant:我不需要在输入参数上使用类型。但是像你建议的那样输入和输出类型参数不会有任何问题。
  • @John M Gant:@Justin Niessner 使用 typeof(T),而我使用的是 instance.GetType()。我不认为这有什么不同。
【解决方案2】:

这是使用字典的简单方法:

    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;
    }

【讨论】:

  • 不错,你甚至可以使用反射来生成代码。唯一需要注意的是缺乏通用性。
  • @ChaosPandion - 是的,我希望返回类型是特定的,但遗憾的是问题中省略了这一点。也许OP可以用更好的东西代替“对象”!
【解决方案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:保持原样。

这是一个合理的选择,因为一旦数字 case 语句达到某个阈值,字符串数据类型的 switch 语句将转换为 Dictionary 查找。在 C# 3.0 编译器上,该阈值是 7。因此,无论有多少 case 语句,查找都是 O(1)。它不会逐个扫描。

【讨论】:

    【解决方案4】:

    您可以使用reflection 在运行时动态获取属性。这是我编写的一个小重新选择实用程序的 sn-p。这是作为扩展方法编写的,可以轻松地让您从类实例中获取属性

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

    【讨论】:

    • 一个缺点,但是(请参阅我对 Justin Niessner 帖子的评论)是,如果返回类型是泛型的,则必须在方法调用中指定它,因为编译器无法推断T 单独使用返回值。因此,您的方法调用实际上是 MyInstance.GetProperty("Title"),或属性的任何类型。
    • 任何通用实现在实践中都会出现问题。如果程序实际上包含myInstance.GetProperty&lt;string&gt;("Title"),那么它还可能包含myInstance.Title。如果您在变量中传递字段,那么您怎么知道它是string
    • @Jeffrey L Whitledge,如果您首先拥有正确类型的实例,那么当他们可以以标准方式调用该属性时,就没有理由进行此练习。通常在这种类型的场景中,您在运行时没有类型信息,但您知道您需要访问一个名为“x”的属性。我也对匿名类型使用了这个实用程序,因为无法转换为匿名类型。
    【解决方案5】:

    好吧,假设 Name 与属性的实际名称匹配(与您的示例不同),这可能最好通过反射来处理。

    【讨论】:

      【解决方案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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2011-03-22
          • 1970-01-01
          • 2012-12-13
          • 2014-10-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-24
          • 2018-08-05
          • 2014-06-19
          相关资源
          最近更新 更多