Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
357 views
in Technique[技术] by (71.8m points)

c# - Why do I have to copy "this" when using LINQ in a struct (and is it OK if I do)?

The code belows contains a simple LINQ query inside an immutable struct.

struct Point
{
   static readonly List</*enum*/> NeighborIndexes;
   //and other readonly fields!

    public IEnumerable<FlatRhombPoint> GetEdges()
    {
        return from neighborIndex in NeighborIndexes;
             select GetEdge(neighborIndex);
    }
}

It does not compile.

Anonymous methods, lambda expressions, and query expressions inside structs cannot access instance members of 'this'. Consider copying 'this' to a local variable outside the anonymous method, lambda expression or query expression and using the local instead.

Does any one know why this is not allowed?

The fix the message suggests works fine:

    public IEnumerable<FlatRhombPoint> GetEdges()
    {
        var thisCopy = this;

        return from neighborIndex in NeighborIndexes;
             select thisCopy.GetEdge(neighborIndex);
    }

But is this standard practice? Are there reasons for not having queries like this in structs? (In the bigger scheme of things making a copy does not worry me performance-wise as such).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Instance methods on structs are called with a reference to thisa hidden ref parameter.
This is why struct methods are able to mutate the structs they're called on.

When you use this (or any other local variable / parameter) inside a lambda expression or LINQ query, the compiler turns it into a field on a compiler-generate closure class.

The CLR does not support ref fields, so it would be impossible for the captured this to work the same way as a regular this. (this is also the reason that you can't use ref parameters inside lambdas)

Iterator methods have the same issue – they are compiled into a hidden enumerator class, and all variables or parameters become fields in the class (this is why iterators cannot take ref parameters).
However, for iterators, C# made the opposite decision. Inside an iterator, you can use this, but it will be copied to a field on the enumerator class.
This means that if you mutate a struct inside an iterator, the mutations will not happen to the caller's copy.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...