【问题标题】:Get struct values from List of class with foreach使用 foreach 从类列表中获取结构值
【发布时间】:2018-07-13 02:15:32
【问题描述】:

我正在尝试从 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 从列表中获取数据时,只有当我告诉列表的确切元素时才有可能

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 一样。

无法将类型“WinFormServer.Memory.MemMainS”隐式转换为 'WinFormServer.Memory.sValues'

【问题讨论】:

  • 你为什么在这里使用ElementAt 根本?你可以直接去:TestList[j].svMode.
  • 您是否试图将MemMainS 类中sValues 类型的命名属性视为一个集合? (即为什么需要反思)
  • 我认为您应该退后一步,专注于您的要求:创建值列表的备份(并可能从备份中重新加载数据)。根据细节,各种序列化技术和数据库可能是解决方案。
  • 这段代码中有多个非常有问题的设计问题;我并不是说您不能 编写那样的代码,但所有这些公共字段和可变结构 迟早会咬你一口。这不是惯用的 C#。
  • 但是问题被标记为 C# ... 你能用 C# 对你的 PLC 进行编程吗?如果不是,您的 C# 环境是什么?为什么它不能连接数据库?我怀疑您以某种方式将数据从 PLC 传输到“正常”环境中的单独 C# 程序,您可以在其中访问文件和数据库...

标签: c# list struct foreach


【解决方案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 地址,但没有属性名称。似乎这些属性名称应该包含在结构数据中。您有一个属性名称,例如 svFreqOrBal,它将与 ObjectName 或地址(例如 Obj2.Addr2)进行 1:1 映射。没有理由不将其作为结构的一部分。

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

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 属性)。

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

【讨论】:

  • 谢谢!这对我帮助很大
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-07
    • 2023-03-12
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多