在不知道类型的情况下在多类型容器上操作

时间:2013-09-08 17:15:13

标签: c# generics containers

我在C#中实现了一种联网的多类型哈希表,我遇到了一些问题。每个条目都有一些与之关联的元数据,如字符串名称和ID号。我的代码中有些地方我正在操作条目,但我不一定需要知道它们包含的类型。因此,例如,在解析更新数据包时,我希望能够做到这样的事情:

entry.Name = ParseName(data);
entry.Id = ParseId(data);
entry = ParseValue(entry, data);
table.Insert(entry);

其中ParseValue()获取条目,根据我从网络获得的数据设置某种“值”参数和某种“类型”参数,并返回通用条目。然后,当我想对数据执行某些操作时,我希望能够执行切换entry.Type之类的操作,然后通过将其转换为entry.Type来使用该值。但每次我尝试做这样的事情时,我最终都需要将类型参数放在我不知道类型的地方。我知道该类型是6种可能类型之一(booldoublestringList<bool>List<double>List<string>),但我不知道它是哪种类型。有没有办法在C#中做到这一点,甚至更好,有没有办法改变我的设计,这不是问题?

注意:上面的所有代码都是对我正在做的事情的简化表示,因为我不想进入解密这些数据包时需要做的麻烦的事情。

编辑:我可能尝试使用它的一个示例(我知道这不是有效的C#)

foreach( entry in table ) {
    switch( entry.Type ) {
        case typeof(bool):
            displayBool( (bool)(entry.Value) );
            break;
        case typeof(double):
            displayDouble( (double)(entry.Value) );
            break;
        case typeof(string):
            displayString( (string)(entry.Value) );
            break;
        case typeof(List<bool>):
            displayListOfBool( (List<bool>)(entry.Value) );
            break;
        case typeof(List<double>):
            displayListOfDouble( (List<double>)(entry.Value) );
            break;
        case typeof(List<string>):
            displayListOfStrings( (List<string>)(entry.Value) );
            break;
    }
}

编辑2:如果有帮助,我正在尝试实施的规范就在这里:http://firstforge.wpi.edu/sf/docman/do/downloadDocument/projects.wpilib/docman.root/doc1318

2 个答案:

答案 0 :(得分:3)

每次看到类似

的内容
 switch( entry.Type )

是时候考虑多态性了。

假设(为了简单起见)您希望执行类似

的操作
switch( entry.Type ) 
{
    case typeof(bool):
        displayBool( (bool)(entry.Value) );
        break; 
    case typeof(double):
        displayDouble( (double)(entry.Value) );
        break;
}

我认为编写

会更清晰
entry.Display();

通过继承和多态来完成这项工作。然后你将拥有一个通用的基本抽象类和其他实现不同Display()的类 - 一个用于你正在使用的每个实际类型。

abstract class BaseEntry<T>
{
   public T Value {get; set;}
   public string Name {get; set;}

   public abstract void Display();
}

class BoolEntry : BaseEntry<bool>
{
     public override void Display()
     {
         // Your code here
     }
}

如果Display()不是Entry应该做的事情,你可以给它一个引用其他对象的东西。

答案 1 :(得分:1)

我喜欢Vadim的方法。你可以这样为你工作:

abstract class BaseEntry
{
    public abstract object Value { get; }
    public string Name { get; set; }

    public abstract void Display();
    public abstract void SetValue(object value);
}

你可以像这样实现它:

class BoolEntry : BaseEntry
{
    public bool ConcreteValue { get; protected set; }

    public override object Value
    {
        get
        {
            return this.ConcreteValue;
        }
    }

    public override void SetValue(object value)
    {
        this.ConcreteValue = (bool)value;        
    }

    public void SetValue(bool value)
    {
        this.ConcreteValue = value; 
    }

    public override void Display()
    {
        Console.WriteLine("Here is your bool: " + this.ConcreteValue.ToString());
    }
}

而不是在Entry类中实现类型约束您可以构建仅允许给定类型的值的哈希表。这是一个例子:

class ConstraintHashTable<TKey, TValue> // Implement all the good interfaces
{
    private HashSet<Type> AllowedTypes;
    private Dictionary<TKey, TValue> Dictionary;

    public TValue this[TKey key]
    {
        get
        {
            return this.Dictionary[key];
        }
    }

    public ConstraintHashTable(HashSet<Type> allowedTypes)
    {
        this.AllowedTypes =
            allowedTypes == null ? new HashSet<Type>() : allowedTypes;

        this.Dictionary = new Dictionary<TKey, TValue>();
    }

    public void Add(TKey key, TValue value)
    {
        if (this.AllowedTypes.Contains(value.GetType()) == false)
        {
            throw new ArgumentException(
                "I don't accept values of type: " + value.GetType().FullName + ".");
        }
        this.Dictionary.Add(key, value);
    }
}

然后你可以创建一个只接受BoolEntry值的哈希表,但是将它保存为BaseEntry,如下所示:

var allowedTypes = new HashSet<Type>(new Type[] { typeof(BoolEntry) });
var cht = new ConstraintHashTable<string, BaseEntry>(allowedTypes);

cht.Add("001", new BoolEntry());
cht["001"].SetValue(true);   
cht["001"].Display();
cht["001"].SetValue(false);
cht["001"].Display();