【问题标题】:Why can't I set properties or use attributes in C# anonymous types?为什么我不能在 C# 匿名类型中设置属性或使用属性?
【发布时间】:2022-04-13 06:09:45
【问题描述】:

C# 匿名类型似乎非常有用,但我很快就找到了一个应用程序,我想在其中设置匿名类型的属性,并且我还希望能够使用属性。

我的应用程序是一个通用的存储过程和 SQL 执行器,它可以自动将 C# 属性映射到带有适当 SqlDbType 的 SQL 输入和输出参数(例如,string 映射到 NVARCHAR(MAX) 等)。

例如

    int PersonID = 1234;
    var output = new { GivenName = (string)null, FamilyName = (string)null };
    sqlExecutor.ExecSQL("SELECT @GivenName = GivenName, @FamilyName = FamilyName FROM People WHERE PersonID = @PersonID", new { PersonID }, output);
    string GivenName = output.GivenName;
    string FamilyName = output.FamilyName;

该方法有效(也适用于存储过程,我只是在上面使用了原始 SQL 以使我想要做的事情更清楚)。 但是我只能通过使用在匿名输出对象中设置支持字段的“坏主意”(使用用户 Alex 对How to set value for property of an anonymous object? 的答案中的代码),使其完全按照上面的形式工作。

我想不出任何其他方法来为此类问题创建如此易于使用、轻量级的界面。很容易看出属性在这里也可能有用,例如修改参数映射。

那么为什么匿名类型被限制为没有设置和属性呢?考虑到基本的匿名类型特性已经在语言中,它们似乎都在合理的用例中很有用,而且它们似乎很容易包含在内。

【问题讨论】:

  • dynamic 在这里会更好吗?
  • 与所有语言功能一样,这可能可以在编译器中工作。但也总是有相关的成本。然后考虑一下执行此操作的语法可能是什么样的——当你硬塞到以这种方式标记匿名类型所需的任何东西时,你还不如创建一个类。
  • 匿名类型属性是只读的,不能设置。
  • 主要原因是它们可以用作分组、连接等中的键值。您是否有理由不只是创建可以的实际类型有属性吗?如果您想为 mutable 匿名类型提出语法,请随时将其发布到 connect.microsoft.com
  • @bmju 是的,技术上可能有可变匿名类型,但也有缺点 - 其中最大的缺点是用作哈希键,这是驱动力之一首先是该功能背后的力量。

标签: c# anonymous-types sqlclient


【解决方案1】:

也许您可以在这里破解另一个解决方案,例如通过反射重写或使用 ExpandoObject 和/或 Dictionary 等动态内容来修改匿名类/实例的生成代码。但这里有一种更简洁、更简单的方法——“with expression”——它提供了“非破坏性突变”。它保留了“类型”,并且仍然允许您改变匿名对象(实际上它会将所有内容复制到一个新对象中(如果您的某些匿名类型道具是引用类型,则会复制一个引用)。

只有在 C# 10 中(尤其是匿名类型)才支持 with 表达式,这意味着 .NET 6 框架。所以这里的解决方案只适用于较新的解决方案类型。我们中的许多人都被 .NET Framework 4.8 中的遗留代码所困扰,等等在现实世界的工作解决方案中。如果您可以在工作中使用 C# 10 和 .NET 6,那么认为自己很幸运,因为您手头有一个更强大的工具带用 C# !

您可以从 C# 10 开始对匿名类型使用 with 表达式(以及结构,在 C# 9 中用于记录)。

例子:

粘贴到 Linqpad 7:此代码在 C# 程序和 .NET 中设置为 Auto

void Main()
{
    var someInstanceOfAnonymousType = new {
     Knight = "Sir Galahad",
     FavoriteColor = "Blue. No RED!",
     Shout = "Aaaargh!"
    }; 
    
    var mutatedInstanceOfAnonymousType = someInstanceOfAnonymousType with
    { Knight = "Sir Lancelot", Shout = "I did not vote for you!" };
    
    mutatedInstanceOfAnonymousType.Dump(); 
}

请注意,with 运算符将维护您的匿名类型,并且仍然允许您以简单的方式修改在匿名类型中公开为只读属性的任意数量的支持字段。只需在 RHS 上键入实例,后跟一个“with”,然后是花括号和带有您要设置的值的属性。

【讨论】:

  • 如果您使用 IlSpy 之类的工具,您可以通过反射找到“隐藏字段”并对其进行变异。完全不建议这样做,只是为了说明这是可能的。 var knightField = mutatedInstanceOfAnonymousType.GetType().GetField("i__Field", BindingFlags.NonPublic | BindingFlags.Instance); knightField.SetValue(mutatedInstanceOfAnonymousType, "高文爵士");
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-20
  • 1970-01-01
  • 2011-12-29
相关资源
最近更新 更多