Object passed by reference unexpectedly

Why is the context of a modified when context is modified?
c#
TokenContext context = new TokenContext(0,1,0,0,src); // start = 0
Token a = new Token(new SortedSet<int>{0,1}, new List<Token>{}, context);
context.start = 1;
context.end = 2;
Token b = new Token(new SortedSet<int>{1,2}, new List<Token>{}, context);
Console.WriteLine(a.context.start); // 1
Console.WriteLine(b.context.start); // 1
c#
TokenContext context = new TokenContext(0,1,0,0,src); // start = 0
Token a = new Token(new SortedSet<int>{0,1}, new List<Token>{}, context);
context.start = 1;
context.end = 2;
Token b = new Token(new SortedSet<int>{1,2}, new List<Token>{}, context);
Console.WriteLine(a.context.start); // 1
Console.WriteLine(b.context.start); // 1
21 Replies
333fred
333fred4d ago
Presumably, TokenContext is a class, yes?
Big Chungus
Big ChungusOP4d ago
yes
c#
public class TokenContext {
public Source source;
public int start;
public int end;
public int line;
public int col;
public TokenContext(int start, int end, int line, int col, Source source) {
this.source = source;
this.start = start;
this.end = end;
this.line = line;
this.col = col;
}
}
c#
public class TokenContext {
public Source source;
public int start;
public int end;
public int line;
public int col;
public TokenContext(int start, int end, int line, int col, Source source) {
this.source = source;
this.start = start;
this.end = end;
this.line = line;
this.col = col;
}
}
c#
public class Token {
public SortedSet<int> types;
public List<Token> children;
public string text;
public TokenContext context;

public Token(SortedSet<int> types, List<Token> children, TokenContext ctx) {
this.types = types;
this.children = children;
this.text = ctx.source.GetText().Slice(ctx.start, ctx.end - ctx.start).ToString();
this.context = ctx;
}
...
}
c#
public class Token {
public SortedSet<int> types;
public List<Token> children;
public string text;
public TokenContext context;

public Token(SortedSet<int> types, List<Token> children, TokenContext ctx) {
this.types = types;
this.children = children;
this.text = ctx.source.GetText().Slice(ctx.start, ctx.end - ctx.start).ToString();
this.context = ctx;
}
...
}
333fred
333fred4d ago
Well, that's how classes work Classes are also known as reference types Do you know C?
Big Chungus
Big ChungusOP4d ago
yes, I'm used to c++
333fred
333fred4d ago
Ok, so TokenContext context is actually TokenContext* context In C#, that's the difference between a struct and a class A class is a reference type, and always allocated on the heap A variable referring to one is actually a pointer to heap data
Big Chungus
Big ChungusOP4d ago
right, so I should almost never use classes then I suppose
333fred
333fred4d ago
No, that is exactly the wrong approach C# is a GC language. Automatic memory management is the whole point
Big Chungus
Big ChungusOP4d ago
so then what should I do when I want a copy of an object?
333fred
333fred4d ago
Depends on the context. In your case here, you'd probably take one of a few possible approaches: 1. Use a record for TokenContext, it probably should be one anyways. Then you wouldn't reassign individual properties like you're doing there, you'd with to do non-destructive mutation 2. Use a builder for TokenContexts. That's effectively what you're doing here, you're treating TokenContext as if it was a builder. But combining these two things into one isn't great; you've opened yourself up to bugs because TokenContext is mutable. 3. You could certainly make TokenContext a struct here. It's not the worst idea, given that there's only 5 pieces of data and they can presumably be enregistered by the JIT. In any of these options, though, I would absolutely make TokenContext immutable. After you create a Token, there's almost certainly no reason to permit modification of TokenContext
Big Chungus
Big ChungusOP4d ago
welp, I'm well out my depth here. I'll probably just use struct since it makes the most sense to me, even if it isn't the most performant I'm interested to know what I should be using for Token tho
333fred
333fred4d ago
Nearly certainly class But I would say you're coding very much like this is C++, not C# We don't do public fields in C#, we use properties, so that we abstract whether things are actually fields
Big Chungus
Big ChungusOP4d ago
not sure what that means
333fred
333fred4d ago
Do you know what properties are?
Big Chungus
Big ChungusOP4d ago
nope
333fred
333fred4d ago
Alright. So, my suggestion is to do a few intro to C# things Clearly, you know about programming, so you don't need the hello world, I've never seen a line of code in my life But you should really learn about the basic building blocks of C#, like "what is a class", "what is a property", etc
333fred
333fred4d ago
Overview - A tour of C#
New to C#? Learn the basics of the language. Start with this overview.
Big Chungus
Big ChungusOP4d ago
yup, this is OOP hell
333fred
333fred4d ago
Being perfectly honest: if that's your first reaction, why are you using C#? C# is a multiparadigm language, but types are at its core.
Big Chungus
Big ChungusOP4d ago
because I need cross platform support and while my c++ code would probably work on mostly any system, compiling it would be a pain
this_is_pain
this_is_pain4d ago
it's not that bad
Unknown User
Unknown User3d ago
Message Not Public
Sign In & Join Server To View

Did you find this page helpful?