使用foreach从类的List中获取struct值

时间:2018-02-02 14:42:44

标签: c# list struct foreach

我试图从PLC获取一些值(每个值都有一个名称和一个int值)并在PC上保存它们以备份。

所以我得到了一个名称和int的结构

public struct sValues
{
    public string ObjectName;
    public int Value;
}

由于我需要存储许多值,因此我还有一个包含所有这些值的类。

public class MemMainS
{
    public sValues svMode;
    public sValues svFreqOrBal;
    (...)
}

和班级列表

MemMainS mainCurrent = new MemMainS();
public List<MemMainS> TestList = new List<MemMainS>();

我也有一些测试值

private void SetTest()
{
    mainCurrent.svMode.ObjectName = "Obj1.Addr1";
    mainCurrent.svMode.Value = 1;

    mainCurrent.svFreqOrBal.ObjectName = "Obj2.Addr2";
    mainCurrent.svFreqOrBal.Value = 2;
}

当我尝试使用foreach从List中获取数据时,只有当我告诉List的确切元素时才可以这样做

foreach (var mv in TestList)
{
    FieldInfo[] listFields = mv.GetType().GetFields(BindingFlags.Public 
                                                  | BindingFlags.NonPublic
                                                  | BindingFlags.Instance);
    int j = 0;
    foreach (FieldInfo lf in listFields)
    {
        FieldInfo[] classFields = lf.FieldType.GetFields(BindingFlags.Public
                                                       | BindingFlags.NonPublic
                                                       | BindingFlags.Instance);

        foreach(FieldInfo cf in classFields)
        {
            sValues sv;

            //sv = TestList.ElementAt(j);//Possible to get it working like this?

            sv = TestList.ElementAt(j).svMode;//Works for just svMode
            //Output:
            //Obj1.Addr1
            //1
        }
        j++;
    }
}

我错过了一些我没有想过的东西吗?或者甚至可能吗?

如果我尝试 sv = TestList.ElemntAt(j); VS编译器告诉我类型不同,但类型是sValues,因为sv是。

  

无法隐式转换类型&#39; WinFormServer.Memory.MemMainS&#39;至   &#39; WinFormServer.Memory.sValues&#39;

2 个答案:

答案 0 :(得分:1)

列出List<MemMainS>;因此列表的第j个元素是MemMainS。您正尝试将其分配给svsValues值。这默认不起作用;你可以添加隐式转换运算符,但你真的可能不应该。但是如果你搜索“C#重载隐式转换运算符”它会告诉你如何。只访问第j个元素的.svMode成员要容易得多。

答案 1 :(得分:1)

我建议利用.Net中的索引器属性来修改MemMainS类,使其行为类似于Dictionary集合,如下所示:

public class MemMainS
{
    private Dictionary<string, sValues> data = new Dictionary<string, sValues>();
    public ICollection<string> Keys {get {return data.Keys;} }

    public Dictionary<string, sValues>.ValueCollection.Enumerator GetEnumerator()
    {
        return data.Values.GetEnumerator();
    }

    public sValues this[string valueName] 
    {
        get 
        {
            if (data.ContainsKey(valueName)) return data[valueName];
            return default(sValues); // could opt to throw an exception here instead
        }
        set 
        {
            data[valueName] = value;
        }
    }

    public sValues svMode {get {return data["svMode"]; } set {data["svMode"] = value;} }
    public sValues svFreqOrBal {get {return data["svFreqOrBal "]; } set {data["svFreqOrBal "] = value;} };
   // (...)
}

这会让你像这样重写循环以避免反思:

foreach (var mv in TestList)
{
    foreach(string item in mv.Keys)
    {
       sValues sv = mv[item];
    }
}

或者像这样:

foreach(var mv in TestList)
{
     foreach(sValues sv in mv)
     {
         //...
     }
}

这里的问题是我们不知道我们在这些循环中查看的 sValue属性。我们有ObjectName地址,但没有属性名称。看起来这些属性名称应该包含在struct数据中。您有一个属性名称,如svFreqOrBal,它将具有1:1映射到ObjectName或地址,如Obj2.Addr2。没有理由不将其作为结构的一部分。

虽然我在这里,但我建议使用简单的不可变模式来定义结构:

public struct sValues
{
    public string ObjectName {get;private set;}
    public int Value {get; private set;}

    public sValues(string objectName, int Value)
    {
        ObjectName = objectName;
        this.Value = Value;
    }
}

(当然,如上所述可能添加了name属性。)

最后,在评论中看到您有超过1000个潜在的不同PLC值,您可能想要完全放弃MemMainS中的命名属性,并且只使用Dictionary + indexer。当然,这会花费您一些编译时的安全性,但您可以通过获取有效PLC值名称的列表或字符串[]来弥补其中的一部分,您可以在索引器中进行检查以进行验证。