C#字段存储在数组中时不保留值

本文关键字:保留 数组 字段 存储 | 更新日期: 2023-09-27 18:21:38

我的程序中有一种情况,大约有20个字段。所有这些都是字符串。现在我还有一个大小为20的字符串数组。我想从存储在这个数组中的字符串中顺序初始化这些字段。我不想这样做:

field1=数组[0];

field2=数组[1];

field20=数组[19];

所以我写了一个这样的测试程序,但它不适用于字符串。它只适用于引用类型。有什么办法吗?

public class Program
{
    private string name;
    private string id;
    private void Func()
    {
        var array = new[] {name, id};
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = "some string";
        }
    }
    public static void Main(string[] args)
    {
        var p = new Program();
        p.Func();
        Console.WriteLine(p.name); // prints null
    }
}

C#字段存储在数组中时不保留值

关于行为的假设是不正确的。

// Create a NEW array with the specified expressions which are evaluated
// immediately (to the current values of the fields)..
var array = new[] {name, id};
// Meaning it is equivalent to this .. note that the field names have
// NOTHING to do with the array object itself.
var array = new[] {(string)null, (string)null};
// Then for each item in the array, assign it a value
// (replacing what was already there anyway)
for (int i = 0; i < array.Length; i++) {
    array[i] = "some string";
}

最后,我们得到了一个看起来像["some string", "some string"]的数组。同样,与字段之间没有"联系"。这个问题与引用类型无关(字符串也是引用类型)。

最好的(通常也是正确的)方法是诚实地用不同的方式来做。尽管可以包装字段访问/设置程序,但始终存在反射。。如果确实需要动态名称,那么可能应该使用字典或类似的名称来代替


下面是一个使用代理来包装赋值操作的方法。此示例使用Actions和"Statement Lambda"语法。

var setters = new Dictionary<string, Action<Program, string>>() {
    { "name", (p, value) => p.name = value },
    { "id", (p, value) => p.id = value },
};
// For each field setter, assign a value (could use the name as a look-up)
foreach (var setter in setters.Values) {
    setter(this, "some string");
}

这是因为setter(..)调用了前面定义的实际分配给适当成员的操作。对于某些情况,使用这样的查找/代理(使用操作、函数或更复杂的类型)是一种有效的方法,但在不需要时可能应该避免。


也可以通过反射来完成。

var t = this.GetType();
var fieldNames = new [] { "name", "id" };
var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
foreach (var name in fieldNames) {
   var fieldInfo = t.GetField(name, bindingFlags);
   fieldInfo.SetValue(this, "some string");
}

反思通常应该是一种"万不得已的方法"。它会丢失静态类型信息,将许多错误推到运行时,并带来性能损失(当然,这可能无关紧要)。有一些非常漂亮的事情可以做(尤其是当表达式和注释也被使用时)。。但反射是神奇的,最好留到真正需要的时候。

看看这是否是您想要实现的目标:

public class Program
{
    private string name;
    private string id;
    private Dictionary<string, int> mapper = new Dictionary<string, int>();
    private String[] array= null;
    public Program()
    {
        mapper.Add("name", 1);
        mapper.Add("id", 2);
    }

    public string Name
    {
        get { return array[mapper["name"]]; }
    }
    public string Id
    {
        get { return array[mapper["id"]]; }
    }
    private void Func()
    {
        array = new[] { name, id };
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = "some string";
        }
    }
    public static void Main(string[] args)
    {
        var p = new Program();
        p.Func();
        Console.WriteLine(p.name); // prints null
    }
}