C# 匿名类型的非只读替代品

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9043848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 05:43:28  来源:igfitidea点击:

Non-read only alternative to anonymous types

c#data-structuresstructanonymous-types

提问by Superbest

In C#, an anonymous type can be as follows:

在 C# 中,匿名类型可以如下所示:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         // Do stuff             
     }
}

However, the following will not compile:

但是,以下内容不会编译:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         myVar.b = true;
     }
}

This is because myVar's fields are read-only and cannot be assigned to. It seems wanting to do something like the latter is fairly common; perhaps the best solution I've seen is to just define a struct outside the method.

这是因为 myVar 的字段是只读的,不能分配给。似乎想要做类似后者的事情是相当普遍的;也许我见过的最好的解决方案是在方法之外定义一个结构体。

However, is there really no other way to make the above block work? The reason it bothers me is, myVar is a local variable of this field, so it seems like it should only be referred to inside the method that uses it. Besides, needing to place the struct outside of the method can make the declaration of an object quite far from its use, especially in a long method.

但是,真的没有其他方法可以使上述块起作用吗?它困扰我的原因是,myVar 是该字段的局部变量,因此似乎只能在使用它的方法内部引用它。此外,需要将结构体放置在方法之外会使对象的声明与其使用相去甚远,尤其是在长方法中。

Put in another way, is there an alternative to anonymous types which will allow me to define a "struct" like this (I realize struct exists in C# and must be defined outside of a method) without making it read-only? If no, is there something fundamentally wrong with wanting to do this, and should I be using a different approach?

换句话说,是否有匿名类型的替代方案,它允许我定义这样的“结构”(我意识到结构存在于 C# 中并且必须在方法之外定义)而不使其成为只读?如果不是,那么想要这样做是否存在根本性的错误,我应该使用不同的方法吗?

采纳答案by Jon Skeet

No, you'll have to create your own class or struct to do this (preferrably a class if you want it to be mutable - mutable structs are horrible).

不,您必须创建自己的类或结构来执行此操作(如果您希望它是可变的,最好是一个类 - 可变结构很糟糕)。

If you don't care about Equals/ToString/GetHashCodeimplementations, that's pretty easy:

如果你不关心Equals/ ToString/GetHashCode实现,这是相当容易:

public class MyClass {
    public bool Foo { get; set; }
    public bool Bar { get; set; }
}

(I'd still use properties rather than fields, for various reasons.)

出于各种原因,我仍然会使用属性而不是字段。)

Personally I usually find myself wanting an immutabletype which I can pass between methods etc - I want a named version of the existing anonymous type feature...

就我个人而言,我通常发现自己想要一个可以在方法等之间传递的不可变类型 - 我想要现有匿名类型功能的命名版本......

回答by supercat

For the above types of operation, you should define your own mutable STRUCT. Mutable structs may pose a headache for compiler writers like Eric Lippert, and there are some unfortunate limitations in how .net handles them, but nonetheless the semantics of mutable "Plain Old Data" structs (structs in which all fields are public, and the only public functions which write thisare constructors, or are called exclusively from constructors) offer far clearer semantics than can be achieved via classes.

对于上述类型的操作,您应该定义自己的可变STRUCT。可变结构可能会让像 Eric Lippert 这样的编译器作者头疼,而且 .net 处理它们的方式有一些不幸的限制,但是可变的“Plain Old Data”结构(所有字段都是公共的,并且唯一写this为构造函数或专门从构造函数调用的公共函数提供比通过类实现的语义要清晰得多的语义。

For example, consider the following:

例如,请考虑以下情况:

struct Foo {
  public int bar; 
  ...other stuff;
}
int test(Action<Foo[]> proc1, Action<Foo> proc2)
{
  foo myFoos[] = new Foo[100];
  proc1(myFoos);
  myFoos[4].bar = 9;
  proc2(myFoos[4]); // Pass-by-value
  return myFoos[4].bar;
}

Assuming there's no unsafe code and that the passed-in delegates can be called and will return in finite time, what will test()return? The fact that Foois a struct with a public field baris sufficient to answer the question: it will return 9, regardless of what else appears in the declaration of Foo, and regardless of what functions are passed in proc1and proc2. If Foowere a class, one would have to examine every single Action<Foo[]>and Action<Foo>that exists, or will ever exist, to know what test()would return. Determining that Foois a struct with public field barseems much easier than examining all past and future functions that might get passed in.

假设没有不安全的代码并且传入的委托可以被调用并且会在有限的时间内test()返回,那么会返回什么?Foo具有公共字段的结构这一事实bar足以回答这个问题:无论 的声明中还出现什么Foo,也不管传入proc1和传入的函数是什么,它都会返回 9 proc2。如果Foo是一个阶级,一个人必须要检查每一个Action<Foo[]>Action<Foo>存在,或将永远存在,知道什么test()会回来。确定它Foo是具有公共字段的结构bar似乎比检查可能传入的所有过去和未来函数容易得多。

Struct methods which modify thisare handled particularly poorly in .net, so if one needs to use a method to modify a struct, it's almost certainly better to use one of these patterns:

this在 .net 中,修改结构的方法处理得特别差,所以如果需要使用一种方法来修改结构,那么使用以下模式之一几乎肯定会更好:

  myStruct = myStruct.ModifiedInSomeFashion(...);  // Approach #1
  myStructType.ModifyInSomeFashion(ref myStruct, ...);  // Approach #2

than the pattern:

比模式:

  myStruct.ModifyInSomeFashion(...);

Provided one uses the above approach to struct-modifying patterns, however, mutable structs have the advantage of allowing code which is both more efficient and easier to read than immutable structs or immutable classes, and is much less trouble-prone than mutable classes. For things which represent an aggregation of values, with no identity outside the values they contain, mutable class types are often the worst possible representation.

如果使用上述方法来修改结构体模式,但是,可变结构体的优点是允许代码比不可变结构体或不可变类更有效且更易于阅读,并且比可变类更不容易出现问题。对于表示值聚合的事物,在它们包含的值之外没有身份,可变类类型通常是最糟糕的表示。

回答by Eric Lippert

Is there an alternative to anonymous types which will allow me to concisely define a simple "record" type like this without making it read-only?

是否有匿名类型的替代方法,它可以让我简洁地定义一个像这样的简单“记录”类型而不会使其成为只读类型?

No. You'll have to make a nominal type.

不,您必须制作一个标称类型。

If no, is there something fundamentally wrong with wanting to do this?

如果不是,那么想要这样做有什么根本性的错误吗?

No, it's a reasonable feature that we have considered before.

不,这是我们之前考虑过的合理功能。

I note that in Visual Basic, anonymous types aremutable if you want them to be.

我注意到在 Visual Basic 中,匿名类型可变的,如果您希望它们可变。

The only thing that is really "fundamentally wrong" about a mutable anonymous type is that it would be dangerous to use one as a hash key. We designed anonymous types with the assumptions that (1) you're going to use them as the keys in equijoins in LINQ query comprehensions, and (2) in LINQ-to-Objects and other implementations, joins will be implemented using hash tables. Therefore anonymous types should be useful as hash keys, and mutable hash keys are dangerous.

关于可变匿名类型的唯一真正“根本错误”的是,将其用作散列键是危险的。我们设计匿名类型的假设是:(1) 您将在 LINQ 查询推导式中将它们用作等值联接中的键,以及 (2) 在 LINQ-to-Objects 和其他实现中,联接将使用哈希表来实现。因此匿名类型应该用作散列键,而可变散列键是危险的。

In Visual Basic, the GetHashCode implementation does not consume any information from mutable fields of anonymous types. Though that is a reasonable compromise, we simply decided that in C# the extra complexity wasn't worth the effort.

在 Visual Basic 中,GetHashCode 实现不使用来自匿名类型的可变字段的任何信息。虽然这是一个合理的妥协,但我们只是认为在 C# 中额外的复杂性不值得付出努力。

回答by AnthonyOSX

You won't be able to get the nice initialization syntax but the ExpandoObjectclass introduced in .NET 4 would serve as a viable solution.

您将无法获得漂亮的初始化语法,但ExpandoObject.NET 4 中引入的类将作为一个可行的解决方案。

dynamic eo = new ExpandoObject();

eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine

回答by Kees C. Bakker

In C# 7 we can leverage named tuplesto do the trick:

在 C# 7 中,我们可以利用命名元组来做到这一点:

(bool a, bool b) myVar = (false, true);

if (myVar.a)
{
    myVar.b = true;
}

回答by LMK

I find it really annoying that you can't set anonymous properties as read/write as you can in VB - often I want to return data from a database using EF/LINQ projection, and then do some massaging of the data in c# that can't be done at the database for whatever reason. The easiest way to do this is to iterate over existing anonymous instances and update properties as you go. NOTE this is not so bad now in EF.Core, as you can mix db functions and .net functions in a single query finally.

我发现你不能像在 VB 中那样将匿名属性设置为读/写真的很烦人 - 通常我想使用 EF/LINQ 投影从数据库中返回数据,然后在 c# 中对数据进行一些按摩,这可以'无论出于何种原因都不能在数据库中完成。最简单的方法是迭代现有的匿名实例并随时更新属性。注意这在 EF.Core 中现在还不错,因为您最终可以在单个查询中混合 db 函数和 .net 函数。

My go-to workaround is to use reflection and will be frowned upon and down-voted but works; buyer beware if the underlying implementation changes and all your code breaks.

我的首选解决方法是使用反射,并且会被拒绝和否决但有效;如果底层实现发生变化并且您的所有代码都中断,则买家要小心。

public static class AnonClassHelper {

    public static void SetField<T>(object anonClass, string fieldName, T value) {
        var field = anonClass.GetType().GetField($"<{fieldName}>i__Field", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        field.SetValue(anonClass, value);
    }

}
// usage
AnonClassHelper.SetField(inst, nameof(inst.SomeField), newVal);

An alternative I have used when dealing with strings is to make properties of type StringBuilder, then these individual properties will be settable via the StringBuilder methods after you have an instance of your anonymous type.

我在处理字符串时使用的另一种方法是创建 StringBuilder 类型的属性,然后在您拥有匿名类型的实例后,可以通过 StringBuilder 方法设置这些单独的属性。