列表有多种类型

时间:2015-05-27 23:32:09

标签: c# .net

在C#中,有没有办法创建一个包含多种类型的list?我的列表项可以是intstringDateTimechar。我知道List<object>ArrayList的使用,但由于封装,这些都不是好的做法。有没有一个很好的方法来实现这一目标?我认为创建界面可能会有所帮助,但我无法找到方法。

4 个答案:

答案 0 :(得分:2)

就像其他海报所说,在你的情况下,我认为List<object>可能很好,但我决定看看我是否可以提出一些东西,所以这里是一个“非现实世界”(即那里有)没有充分理由这样做,所以请不要将它粘贴到您的生产代码中)示例:

void Main()
{
    var items = new Items();
    items.Add(1);
    items.Add("foo");
    items.Add(DateTime.Now);
    items.Add('x');

    foreach (var item in items)
    {
        Console.WriteLine (item);
    }
}

enum Kind
{
    Int,
    String,
    DateTime,
    Char
}

class Items : IEnumerable<object>
{
    Stack<int> Ints = new Stack<int>();
    Stack<string> Strings = new Stack<string>();
    Stack<DateTime> DateTimes = new Stack<DateTime>();
    Stack<char> Chars = new Stack<char>();
    List<Kind> Kinds = new List<Kind>();

    IEnumerator<object> System.Collections.Generic.IEnumerable<object>.GetEnumerator()
    {
        foreach (var kind in Kinds)
        {
            switch (kind)
            {
                case Kind.Int:
                    yield return Ints.Pop();
                    break;
                case Kind.String:
                    yield return Strings.Pop();
                    break;
                case Kind.DateTime:
                    yield return DateTimes.Pop();
                    break;
                case Kind.Char:
                    yield return Chars.Pop();
                    break;
            }
        }
    }

    IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return (this as System.Collections.Generic.IEnumerable<object>).GetEnumerator();
    }

    public void Add(int i)
    {
        Ints.Push(i);
        Kinds.Add(Kind.Int);
    }

    public void Add(string s)
    {
        Strings.Push(s);
        Kinds.Add(Kind.String);
    }

    public void Add(DateTime dt)
    {
        DateTimes.Push(dt);
        Kinds.Add(Kind.DateTime);
    }

    public void Add(char c)
    {
        Chars.Push(c);
        Kinds.Add(Kind.Char);
    }
}

答案 1 :(得分:1)

你最好的选择是制作一个List<object>并每次施放它或制作这样的包装:

class Either<TRight, TLeft> 
{
    private TLeft Left {get; private set;}
    private TRight Right {get; private set;}
    private bool IsLeft {get; private set;}        
}

我没有添加构造函数或其他所需的东西,我希望这很直接。我从F#和函数式语言中得到了这个想法,如果你想改进它,你可以稍后检查。

然后制作一个List<Either<DateTime,char>>,并在每种情况下询问它是DateTime还是char并按照您的方式行事

答案 2 :(得分:1)

IIRC的

IConvertible由所有类型实施,因此您可以接受List<IConvertible>。其他类型也实现了IConvertible,但没有object那么多(当然,这几乎是.NET中的所有类型)。这使您可以使用内置的IConvertible方法根据需要在类型之间切换。

另一个简单的方法是List<string>,然后只是ToString()所有这些(IConvertible强制执行特定于文化的ToString()重载,并且所有内置的IConvertible也覆盖Object.ToString())。你将性能提升为字符串转换并且解析相对昂贵,并且你必须尝试将字符串解析为每个原语,然后才能确定它实际上只是一个字符串,所以我不会&#39 ;对于必须高效处理的大型列表,建议使用此方法。还有失去真实类型的可能性;如果你添加字符串&#34; 12345&#34;在列表中,解析器会认为它最初是一个int,尽管你传递的只是一个字符串。

我认为包装是你最好的选择。最好的内置是Tuple;你可以定义一个List<Tuple<int,string,DateTime,char,int>>,其中最后一个int参数标识元组的填充字段(1 = int,2 = string等),其他所有参数都有默认值。您可以使用Item5动态检索Item1到Item4,或者只是将它放在switch语句中。您可以将列表和所有这些元组打包并解压缩到列表中运行的扩展方法中,或者通过派生或包含另一个类中的列表。

作为前两种方法的混合,您可以封装类型信息和字符串表示:Tuple<string, ElementType>(其中ElementType是一个枚举,表示字符串表示最初可能已经存在的类型),这消除了猜测;只需解析回指定的类型。

答案 3 :(得分:1)

我提出了第二种但又截然不同的方法(可能在其他答案中描述但现在在这里实现),这次利用隐式操作,并且还能够使用框架List类型:

void Main()
{
    var list = new List<Item>
    {
        1,
        "foo",
        DateTime.Now,
        'x'
    };
    foreach (var item in list)
    {
        Console.WriteLine (item.ToString());
    }
    int i = list[0];
    string s = list[1];
    DateTime dt = list[2];
    char c = list[3];
    Console.WriteLine ("int: {0}, string: {1}, DateTime: {2}, char: {3}", i, s, dt, c);
}

enum Kind
{
    Int,
    String,
    DateTime,
    Char
}

class Item
{
    int intValue;
    string stringValue;
    DateTime dateTimeValue;
    char charValue;
    Kind kind;

    public object Value
    {
        get
        {
            switch (kind)
            {           
                case Kind.Int:
                    return intValue;
                case Kind.String:
                    return stringValue;
                case Kind.DateTime:
                    return dateTimeValue;
                case Kind.Char:
                    return charValue;
                default:
                    return null;
            }

        }
    }

    public override string ToString()
    {   
        return Value.ToString();
    }

    // Implicit construction
    public static implicit operator Item(int i)
    {
        return new Item { intValue = i, kind = Kind.Int };
    }
    public static implicit operator Item(string s)
    {
        return new Item { stringValue = s, kind = Kind.String };
    }
    public static implicit operator Item(DateTime dt)
    {
        return new Item { dateTimeValue = dt, kind = Kind.DateTime };
    }
    public static implicit operator Item(char c)
    {
        return new Item { charValue = c, kind = Kind.Char };
    }

    // Implicit value reference
    public static implicit operator int(Item item)
    {
        if(item.kind != Kind.Int) // Optionally, you could validate the usage
        {
            throw new InvalidCastException("Trying to use a " + item.kind + " as an int");
        }
        return item.intValue;
    }
    public static implicit operator string(Item item)
    {
        return item.stringValue;
    }
    public static implicit operator DateTime(Item item)
    {
        return item.dateTimeValue;
    }
    public static implicit operator char(Item item)
    {
        return item.charValue;
    }
}