【问题标题】:C# Switch on Object Type at RuntimeC# 在运行时打开对象类型
【发布时间】:2011-11-01 06:18:47
【问题描述】:

我有一个List<object>。我想遍历列表并以比o.ToString() 更友好的方式打印值,以防某些对象是布尔值或日期时间等。

您将如何构造一个我可以调用的函数,如 MyToString(o),并返回一个格式正确(由我指定)的字符串以用于其实际类型?

【问题讨论】:

  • 类型有很多,你打算支持哪些?
  • 也许创建一个Dictionary<Type,Func<Object, String>>
  • 你不能打开类型,但是有一些解决方法,根据这个问题:stackoverflow.com/questions/298976/…你可以将该解决方案用于帮助类。
  • 你的意思是像o.GetType().ToString()这样的吗?

标签: c# .net


【解决方案1】:

您可以将dynamic keyword 用于.NET 4.0,因为您正在处理内置类型。否则,您将为此使用多态性。

例子:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<object> stuff = new List<object> { DateTime.Now, true, 666 };
        foreach (object o in stuff)
        {
            dynamic d = o;
            Print(d);
        }
    }

    private static void Print(DateTime d)
    {
        Console.WriteLine("I'm a date"); //replace with your actual implementation
    }

    private static void Print(bool b)
    {
        Console.WriteLine("I'm a bool");
    }

    private static void Print(int i)
    {
        Console.WriteLine("I'm an int");
    }
}

打印出来:

I'm a date
I'm a bool
I'm an int

【讨论】:

  • OP 如何将多态性与 bool 等原始类型一起使用?
  • 这是我的确切观点,你不需要,除非你想从 System.Boolean 继承。
  • 我很困惑:当你写“否则使用多态”时,你的意思是“如果 .NET
  • 否则,我的意思是当您不处理内置类型时。我同意我的措辞有点混乱。
  • dynamic 在性能方面的成本不为零,如果您要在大型列表中使用它,则必须考虑到它。
【解决方案2】:

这取决于设计的重要性:

  • o.GetType()/o.GetType().Name 上的 if 语句或 switch 语句
  • 实现一种 IShow-Interface(使用方法 void Show(object o) 并使用 Dictionary 将类型映射到该接口的实现,仅使用
    if (TryGetValue(o.GetType, out show)) show.Show(o); 
  • 坚持下去,让对象讲述故事(覆盖 ToString)...是的,您不想要这个,但恕我直言,这是 最好的方法

【讨论】:

    【解决方案3】:

    您是否考虑过以更友好的方式覆盖 ToString()?

    【讨论】:

    • 那么,他要创建 MyBooleans、MyIntegers、MyDates 等?如果它们是他自己的类,那么这将是完全适用的,虽然这会起作用,但是为了提供 .ToString() 覆盖而对一堆内置类型进行子类化似乎有点痛苦。合理的答案,但我个人认为我无法做到这一点并对此感到满意。
    【解决方案4】:

    这是一个使用 cmets 的工作示例。它使用通用类型字典和 Lambda 函数。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            // a custom class
            public class MyPerson
            {
                public string FN { get; set; }
                public string LN { get; set; }
            }
            static void Main(string[] args)
            {
                // your prebuilt dictionary of Types to Lambda expressions to get a string
                Dictionary<Type, Func<object, String>> MyToStringLookup = new Dictionary<Type, Func<object, string>>()
                {
    
                    {typeof(String), new Func<Object, String>( obj => obj.ToString() )},
                    {typeof(DateTime), new Func<Object, String>( obj => ((DateTime)obj).ToString("d") )},
                    {typeof(MyPerson), new Func<Object, String>( obj => (obj as MyPerson).LN )},
                };
                // your list of objects
                List<Object> MyObjects = new List<Object>()
                {
                    "abc123",
                    DateTime.Now,
                    new MyPerson(){ FN = "Bob", LN = "Smith"}
                };
                // how you traverse the list of objects and run the custom ToString
                foreach (var obj in MyObjects)
                    if (MyToStringLookup.ContainsKey(obj.GetType()))
                        System.Console.WriteLine(MyToStringLookup[obj.GetType()](obj));
                    else // default if the object doesnt exist in your dictionary
                        System.Console.WriteLine(obj.ToString());
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      这样的事情可能会让你上路:

      private static String MyToString(object o)
      {
          var val = "";
          switch (o.GetType().Name)
          {
              case "Boolean": val = ((bool)o) ? "this is true" : "this is false"; break;
              case "DateTime": val = "The date is: " + ((DateTime)o); break;
              case "Int32": val = "The number-value is: " + (int)o; break;
          }
          return val;
      }
      

      希望这会有所帮助! ;)

      【讨论】:

      • 我会为 val = o.ToString() 添加一个默认值:case,但这是一个干净的程序解决方案。
      【解决方案6】:

      您唯一的选择是等效于 if-else-if 结构。 switch 不允许 switch on 类型,因为 switch 结构需要一个具有互斥值的可枚举集合(并且一个对象可以是多种类型)。

      编辑阿巴斯评论:

      GetType().Name 是有效的,但会导致您在此上下文中出现潜在错误,因为它可以返回您不知道的类型。即使对象存储为类型 A,如果 B 继承 A,GetType().Name 也可能返回“B”。如果您在执行切换的方法的上下文中不知道 B(它可能是来自另一个继承当前库的库,它可能是您编写方法后添加的类型),您将在伪类型切换中错过它。如果你写if(obj is A),你就不会。

      例如,如果我这样写:

      /// <summary>
      /// Displays ":)" if obj is a Foo
      /// </summary>
      public static void CaseType(object obj)
      {
          switch(obj.GetType().Name)
          {
              case "Foo":
                 MessageBox.Show(":)");
                 break;
                   default:
                 MessageBox.Show(":(");
                 break;
          }
      }
      

      评论是谎言,因为

      public class Bar : Foo
      {
      }
      
      public static void CaseTypeSpecialized()
      {
           Foo obj = new Bar();
           CaseType(obj);
      }
      

      将显示“:(”。

      如果你写它会起作用的

      /// <summary>
      /// Displays ":)" if obj is a Foo
      /// </summary>
      public static void CaseType(object obj)
      {
          if (obj is "Foo")
          {
              MessageBox.Show(":)");
          }
          else
          {
              MessageBox.Show(":(");
          }
      }
      

      与非互斥值不兼容的是switch的概念。这就是为什么在值不是独占的情况下打开 Flags 枚举时会出现编译错误的原因。

      【讨论】:

      • 您实际上可以使用switch语句,请参阅我的答案以了解如何使用它! :)
      • 是但不是。我编辑了答案以解释为什么我认为这不是一个好主意。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-13
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多