【问题标题】:Operating on Multi-type Container Without Knowing the Type在不知道类型的情况下操作多类型容器
【发布时间】:2013-09-08 17:15:12
【问题描述】:

我正在用 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

【问题讨论】:

  • “我希望能够做一些事情,比如切换 entry.Type,然后通过将其转换为 entry.Type 来使用该值”你能举个例子吗?很难理解你想做什么
  • 您可以使用继承和多态来解决大多数此类问题,但很难理解您到底想要做什么......

标签: c# generics containers


【解决方案1】:

每次你看到类似的东西

 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 应该做的事情,你可以给它一个对其他能够做这种事情的对象的引用。

【讨论】:

  • 我真的很想使用这种设计,但我正在尝试制作 BaseEntry 的容器,并且要正确地做到这一点,我需要能够访问一些成员,比如名称,不知道类型参数。
  • @kunkelwe,您可以简单地将这些属性提取到另一个非泛型基类(或两者的接口)。然后声明class BaseEntry&lt;T&gt; : BaseEntryBaseEntry&lt;T&gt; 将包含泛型成员(例如Value),BaseEntry 将包含非泛型成员(例如Name)。
【解决方案2】:

我喜欢 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();

【讨论】:

  • 我认为这可能是我能做到的最好的,我需要满足的要求。
猜你喜欢
  • 2012-09-19
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 2017-03-11
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
相关资源
最近更新 更多