在运行时针对对象类型使用C# Switch语句

4

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

你将如何构造一个函数,让我可以像这样调用MyToString(o)并返回正确格式化(由我指定)的字符串,以适应其实际类型?


1
有很多种类型,你打算支持哪些? - Grant Thomas
2
也许可以创建一个 Dictionary<Type,Func<Object, String>> - George Duckett
你不能使用Type进行开关操作,但是根据这个问题:https://dev59.com/qHVC5IYBdhLWcg3wbgdS,有一些解决方法,你可以将该解决方案转化为一个辅助类。 - Russ Clarke
你的意思是类似于 o.GetType().ToString() 吗? - Dylan Meador
6个回答

6
您可以在使用.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如何在布尔等原始类型中使用多态? - Serge Wautier
这正是我的观点,除非你想从 System.Boolean 继承。 - wsanville
我有点困惑:当你写“否则使用多态性”时,你是指“.NET < 4”,对吗? - Serge Wautier
我指的是当你不处理内置类型时。我同意我的措辞有点令人困惑。 - wsanville
动态在性能方面具有非零成本,如果您要在大型列表中使用它,则必须考虑到这一点。 - Evren Kuzucuoglu

1
这是一个带有注释的工作示例。它使用了一个通用的类型字典和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());
        }
    }
}

1

这取决于设计的重要性:

  • 使用if语句或switch语句来判断o.GetType()/o.GetType().Name
  • 实现一种IShow接口(具有void Show(object o)方法),并使用字典将类型映射到此接口的实现,只需使用
    if (TryGetValue(o.GetType, out show)) show.Show(o); 
  • 坚持使用对象自述的方式(重写ToString)...是的,你可能不想这样做,但在我看来,这是最好的方法。

1
你有没有考虑以更友好的方式重写ToString()方法?

那么,他要创建MyBooleans、MyIntegers、MyDates等类吗?如果它们是他自己的类,那么这完全适用,虽然这样做可以工作,但子类化一堆内置类型只是为了提供.ToString()覆盖似乎有点痛苦。这是一个合理的答案,但就个人而言,我不认为我能这样做并感到满意。 - deepee1

0
你唯一的选择是类似于if-else-if结构。switch不允许基于类型进行切换,因为switch结构需要具有互斥值的可枚举集合(而对象可以是多种类型)。
对于Abbas评论的编辑:
在这种情况下,GetType()。Name是有效的,但可能会导致潜在的错误,因为它可能返回一个你不知道的类型。即使一个对象被存储为类型A,GetType()。Name也可能返回“B”,如果B继承了A。如果您不知道在做switch的方法上下文中的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时,如果值不是互斥的,就会出现编译错误。


你实际上可以使用 switch 语句,查看我的答案以了解如何使用它! :) - Abbas
是的,但不完全是。我编辑了我的答案,解释了为什么我认为这不是一个好主意。 - Evren Kuzucuoglu

0

类似这样的东西可能会帮助你入门:

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()添加一个default: case,但这是一个干净的过程式解决方案。 - Louis Ricci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接