Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

linq

struct

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).

like image 446
Herman Tulleken Avatar asked Mar 25 '13 16:03

Herman Tulleken


People also ask

Does LINQ return a copy?

Skip, Take and other method of LINQ does not return any collection. They return IEnumerable interface that allows to get next element from underlying source.

What is the purpose of using LINQ?

LINQ to objects – Allows querying in-memory objects like arrays, lists, generic list and any type of collections. LINQ to XML – Allows querying the XML document by converting the document into XElement objects and then querying using the local execution engine.

Why should I use struct instead of class?

Structs copy the entire value on the assignment, whereas reference types copy the reference on assignment. So, large reference type assignments are cheaper than the value types. Instance field declarations in Struct cannot include variable initializers. But, static fields in Struct can include variable initializers.

Why do we use struct in C#?

The struct (structure) is like a class in C# that is used to store data. However, unlike classes, a struct is a value type. Suppose we want to store the name and age of a person. We can create two variables: name and age and store value.


1 Answers

Instance methods on structs are called with a reference to this – a 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.

like image 91
SLaks Avatar answered Oct 23 '22 12:10

SLaks