С# Включить тип объекта во время выполнения

У меня есть List<object>. Я хочу перебирать список и печатать значения более дружелюбно, чем просто o.ToString(), если некоторые из объектов являются логическими или datetimes и т.д.

Как бы вы структурировали функцию, которую я могу назвать как MyToString(o), и вернуть строку, отформатированную правильно (указанную мной) для ее фактического типа?

Ответ 1

Вы можете использовать динамическое ключевое слово для этого с .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

Ответ 2

что зависит от того, насколько важна конструкция:

  • if или switch-statement для o.GetType()/o.GetType(). Имя
  • реализация своего рода IShow-интерфейса (с помощью метода void Show (object o) и использование словаря для сопоставления типов с реализациями этого интерфейса и просто использование
    if (TryGetValue(o.GetType, out show)) show.Show(o); 
  • просто придерживайтесь его и позволяйте объектам рассказывать историю (переопределять ToString)... да, вы не хотите этого, но IMHO это лучший способ сделать это

Ответ 3

Вы считали, что переопределить ToString() более дружелюбно?

Ответ 4

Вот рабочий пример с комментариями. Он использует общий словарь типа и Lambda Func.

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

Ваш единственный вариант эквивалентен структуре if-else-if. коммутатор не разрешает тип включения, потому что структура коммутатора нуждается в перечислимом ансамбле с взаимоисключающими значениями (и объект может быть нескольких типов).

Изменить комментарий Аббаса:

GetType(). Имя действительно, но приведет к потенциальным ошибкам в этом контексте, потому что он может вернуть тип, который вы не знаете. Даже если объект хранится как тип A, GetType(). Имя может потенциально возвращать "B", если B наследует A. Если вы не знаете 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(":(");
    }
}

Это концепция коммутатора, которая несовместима с не взаимоисключающими значениями. Вот почему вы можете получить компиляцию ошибок при включении перечисления Flags, когда значения не являются эксклюзивами.

Ответ 6

Что-то вроде этого может помочь вам:

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

Надеюсь, это поможет!;)