提问人:dabon 提问时间:5/24/2023 最后编辑:Charliefacedabon 更新时间:5/24/2023 访问量:47
将 DataColumn 重命名为仅更改大小写会导致奇怪的行为
DataColumn renaming to only change casing causes a weird behaviour
问:
当涉及到DataColumn重命名时,我在.NET Framework和.NET中发现了一个奇怪的行为(错误?以下是重现它的方法。
- 在 DataTable 中获取 DataColumn
- 重命名它,以便仅更改其大小写
- 再次将其重命名为其他名称
- 现在,该列将同时以旧名称和新名称提供
- 如果将其重命名回旧名称,则会收到 DuplicateNameException
该问题似乎与 DataColumnCollection 的内部字典有关,当列重命名为仅更改其大小写时,该字典不会更新。
DataTable table = new DataTable();
table.Columns.Add("foo", typeof(string));
// The DataTable contains a single column named "foo"
// The dictionary _columnFromName contains a single entry having its key equal to "foo"
table.Columns["foo"].ColumnName = "FOO";
// The DataTable now contains a single column named "FOO"
// The dictionary _columnFromName did not get updated, so the entry key STILL equal to "foo"
table.Columns["FOO"].ColumnName = "bar";
// The DataTable now contains a single column named "bar"
// Here the DataColumnCollection registers the new columnname "bar", but fails the unregistration of the column name "FOO", because the dictionary does not contain a key equal to "FOO".
// So, the dictionary _columnFromName now contains TWO entries having their keys equal to "foo" and "bar"
// In fact, the column is still available under the old name ...
Console.WriteLine(table.Columns["foo"]);
// ... and of course it is also available under the new name "bar"
Console.WriteLine(table.Columns["bar"]);
// Now, this will throw a DuplicateNameException, because the old dicionary key is still present
table.Columns["bar"].ColumnName = "foo";
下面是面向 .NET Framework 4.7.2 的 .NET Fiddle。您可以将其更改为 .NET 7,但仍会遇到此问题。https://dotnetfiddle.net/vhoV6X
还有其他人遇到过这种行为吗?是故意的吗?Microsoft知道吗?
答:
我找不到任何关于这种行为的官方文档,但我的假设是,一旦设置了列的名称,就不应该改变。如果 Microsoft 现在在不需要向后兼容性的情况下重新实现该类,我希望他们会使属性“init”而不是“set”。
假设您可以重命名列。如果数据表已经包含行,会发生什么情况?应该将值移动到新的列名称中,还是保持不变?
一个类似的例子是 。它无法阻止您修改已添加的对象,即使这样做可能会更改哈希值,从而破坏哈希集的功能(尽管这有据可查)。HashSet
评论
ColumnName
setter 的代码使用 with 来检查是否调用 .string.Compare
ignoreCase: true
table.Columns.RegisterColumnName
if (String.Compare(_columnName, value, true, Locale) != 0) {
// skip...
table.Columns.RegisterColumnName(value, this);
if (_columnName.Length != 0)
table.Columns.UnregisterName(_columnName);
}
因此,只有当名称以不区分大小写的方式更改时,才会更新表。这会产生一个错误,您可以先将其更改为相似的名称(并且不会发生),然后更改不同的名称(失败,因为它找不到它)。Columns
UnregisterName
UnregisterName
UnregisterName
函数如下所示:
internal void UnregisterName(string name) {
columnFromName.Remove(name);
if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, table.Locale) != 0) {
do {
defaultNameIndex--;
} while (defaultNameIndex > 1 &&
!Contains(MakeName(defaultNameIndex - 1)));
}
}
错误在第一行:没有检查是否未能在字典中找到该列。Remove
真正应该发生的事情是,字典应该使用不区分大小写的比较器进行初始化。columnFromName
StringComparer.OrdinalIgnoreCase
评论