Why can iterators in structs modify this? - c#

I discovered that iterator methods in value types are allowed to modify this.
However, due to limitations in the CLR, the modifications are not seen by the calling method. (this is passed by value)
Therefore, identical code in an iterator and a non-iterator produce different results:
static void Main() {
Mutable m1 = new Mutable();
m1.MutateWrong().ToArray(); //Force the iterator to execute
Console.WriteLine("After MutateWrong(): " + m1.Value);
Console.WriteLine();
Mutable m2 = new Mutable();
m2.MutateRight();
Console.WriteLine("After MutateRight(): " + m2.Value);
}
struct Mutable {
public int Value;
public IEnumerable<int> MutateWrong() {
Value = 7;
Console.WriteLine("Inside MutateWrong(): " + Value);
yield break;
}
public IEnumerable<int> MutateRight() {
Value = 7;
Console.WriteLine("Inside MutateRight(): " + Value);
return new int[0];
}
}
Output:
Inside MutateWrong(): 7
After MutateWrong(): 0
Inside MutateRight(): 7
After MutateRight(): 7
Why isn't it a compiler error (or at least warning) to mutate a struct in an iterator?
This behavior is a subtle trap which is not easily understood.
Anonymous methods, which share the same limitation, cannot use this at all.
Note: mutable structs are evil; this should never come up in practice.

In order to justify a warning, it should be in a situation where the programmer is likely to get unexpected results. According to Eric Lippert, "we try to reserve warnings for only those situations where we can say with almost certainty that the code is broken, misleading or useless." Here is an instance where the warning would be misleading.
Let's say you have this perfectly valid – if not terribly useful – object:
struct Number
{
int value;
public Number(int value) { this.value = value; }
public int Value { get { return value; } }
// iterator that mutates "this"
public IEnumerable<int> UpTo(int max)
{
for (; value <= max; value++)
yield return value;
}
}
And you have this loop:
var num = new Number(1);
foreach (var x in num.UpTo(4))
Console.WriteLine(num.Value);
You'd expect this loop to print 1,1,1,1, not 1,2,3,4, right? So the class works exactly as you expect. This is an instance where the warning would be unjustified.
Since this is clearly not a situation where the code is broken, misleading, or useless, how would you propose that the compiler generate an error or warning?

To cite yourself "mutable structs are evil" :)
The same thing as you experienced happens if you implement an extension method for a struct.
If you try to modify the struct within the extension method you still will have your original struct unchanged.
It is a bit less surprising since the extension method signature looks like:
static void DoSideEffects(this MyStruct x) { x.foo = ...
Looking at it we realize that something like parameter passing happens and therefore the struct is copied. But when you use the extension it looks like:
x.DoSideEffects()
and you will be surprised not to have any effects on your variable x.
I suppose that behind the scenes your yield constructs do something similar to extensions.
I would phrase the starting sentence harder:
"structs are evil" .. in general ;)

I had a similar thought to what Gabe said. It seems at least theoretically possible that one might opt to use a struct to behave kind of like a method, encapsulating that method's local variables as instance fields:
struct Evens
{
int _current;
public IEnumerable<int> Go()
{
while (true)
{
yield return _current;
_current += 2;
}
}
}
I mean, it's kind of weird, obviously. It kind of reminds me of ideas I've encountered in the past, though, where developers have concocted ever-stranger ways of calling methods, such as wrapping a method's parameters into an object and letting that object call the method—going backwards, in a sense. I'd say that's roughly what this is.
I'm not saying this would be a wise thing to do, but it is at least a way of using this in the way you are describing that might be intentional, and would technically exhibit correct behavior.

It's not really clear what should happen, though I think .net is deficient in not requiring a special attribute for methods which modify 'this'; such an attribute could be useful applied to immutable class types as well as mutable structs. Methods which are tagged with such an attribute should only be usable on structure variables, fields, and parameters, and not on temporary values.
I don't think there's any way to avoid having the iterator capture the struct by value. It's entirely possible that by the time an iterator is used, the original struct upon which it was based may not exist anymore. On the other hand, if the struct implemented an interface that inherited IEnumerable<int>, but also included a function to return Value, casting the struct to that interface type before using the enumerator could in theory allow the iterator to keep a reference to the struct without having to recopy its value; I wouldn't be at all surprised if the enumerator copies the struct by value even in that case, though.

Related

Is it a principle to always have the most abstract parameters and if so does it have a name?

For example, one might have a method max on an integer array.
public static int Max(int[] ints)
{
var max = ints[0];
foreach(var i in ints)
{
if(i > max)
{
max = i;
}
}
return max;
}
But actually we didn't utilize the fact that were were using an array, just that we could iterate over them, and we didn't need ints, we just needed an object that knows how to compare itself. So a more powerful method might be:
public static T MyMax<T>(IEnumerable<T> ts) where T : IComparable<T>
{
var max = ts.First();
foreach(var i in ts)
{
if(i.CompareTo(max) > 0)
{
max = i;
}
}
return max;
}
And I guess the above discounts the fact that the IEnumerable<T> could be infinite which would cause this to hang indefinitely, but then why are you trying to find the Max fo an infinite iterable in the first place.
The point I'm trying to make is that the second method is much more powerful than the former, as it makes the least assumptions about it's parameters as needed. Is there a name for such a principle? Is this even a thing?
Thanks in advance.
I would call this Programming to an Interface.
The design principle of Programming to an Interface is most commonly mentioned in the context of declaring variables, and that's how the famous Gang of Four book introduced the principle; but it applies equally well to declaring method parameters.
Don't declare variables to be instances of particular concrete classes. Instead, commit only to an interface defined by an abstract class. You will find this to be a common theme of design patterns...
The term variables here can be considered in a broad sense that includes method parameters.

A couple of questions about optimising code for speed in C#

If I have a method that is called many times, such as:
public int CalledManyTimes(int a, int b)
{
MyObject myObject = new myObject();
int c = a + b + myObject.GetSomeValue();
return c;
}
Is there a performance boost by putting MyObject myObject; outside of the method, so it's only declared once, or will the compiler do this automatically?
How exactly are structs passed around?
I'm passing in a Point struct to a method (Point contains only int x, int y), and that method is altering the value and returning a new Point(newX, newY); Is it better to alter the Point that was passed into the method and return that? Or can I create a Point point; outside the method as proposed in my first question and use that?
myObject appears to have no useful state; so: make that a static method - problem solved; no allocation, no virtual call:
public int CalledManyTimes(int a, int b)
{
int c = a + b + MyObject.GetSomeValue(); // static method
return c;
}
For anything else: profile.
Looking at your specific questions:
Is there a performance boost by putting MyObject myObject; outside of the method, so it's only declared once, or will the compiler do this automatically?
Initializing it zero times is even faster. However, if there is some state that isn't obvious in the question, then yes, I would expect it to be more efficient to reuse a single instance - however, that changes the semantic (in the original the state is not shared between iterations).
How exactly are structs passed around?
By default, they are copied on the stack as soon as you so much as glance in their direction. You can use ref to avoid the copy, which may be useful if the struct is massively overweight, or need to be updated (ideally with reassignment, rather than mutability).

Is modifying a value type from within a using statement undefined behavior?

This one's really an offshoot of this question, but I think it deserves its own answer.
According to section 15.13 of the ECMA-334 (on the using statement, below referred to as resource-acquisition):
Local variables declared in a
resource-acquisition are read-only, and shall include an initializer. A
compile-time error occurs if the
embedded statement attempts to modify
these local variables (via assignment
or the ++ and -- operators) or
pass them as ref or out
parameters.
This seems to explain why the code below is illegal.
struct Mutable : IDisposable
{
public int Field;
public void SetField(int value) { Field = value; }
public void Dispose() { }
}
using (var m = new Mutable())
{
// This results in a compiler error.
m.Field = 10;
}
But what about this?
using (var e = new Mutable())
{
// This is doing exactly the same thing, but it compiles and runs just fine.
e.SetField(10);
}
Is the above snippet undefined and/or illegal in C#? If it's legal, what is the relationship between this code and the excerpt from the spec above? If it's illegal, why does it work? Is there some subtle loophole that permits it, or is the fact that it works attributable only to mere luck (so that one shouldn't ever rely on the functionality of such seemingly harmless-looking code)?
I would read the standard in such a way that
using( var m = new Mutable() )
{
m = new Mutable();
}
is forbidden - with reason that seem obious.
Why for the struct Mutable it is not allowed beats me. Because for a class the code is legal and compiles fine...(object type i know..)
Also I do not see a reason why changing the contents of the value type does endanger the RA. Someone care to explain?
Maybe someone doing the syntx checking just misread the standard ;-)
Mario
I suspect the reason it compiles and runs is that SetField(int) is a function call, not an assignment or ref or out parameter call. The compiler has no way of knowing (in general) whether SetField(int) is going to mutate the variable or not.
This appears completely legal according to the spec.
And consider the alternatives. Static analysis to determine whether a given function call is going to mutate a value is clearly cost prohibitive in the C# compiler. The spec is designed to avoid that situation in all cases.
The other alternative would be for C# to not allow any method calls on value type variables declared in a using statement. That might not be a bad idea, since implementing IDisposable on a struct is just asking for trouble anyway. But when the C# language was first developed, I think they had high hopes for using structs in lots of interesting ways (as the GetEnumerator() example that you originally used demonstrates).
To sum it up
struct Mutable : IDisposable
{
public int Field;
public void SetField( int value ) { Field = value; }
public void Dispose() { }
}
class Program
{
protected static readonly Mutable xxx = new Mutable();
static void Main( string[] args )
{
//not allowed by compiler
//xxx.Field = 10;
xxx.SetField( 10 );
//prints out 0 !!!! <--- I do think that this is pretty bad
System.Console.Out.WriteLine( xxx.Field );
using ( var m = new Mutable() )
{
// This results in a compiler error.
//m.Field = 10;
m.SetField( 10 );
//This prints out 10 !!!
System.Console.Out.WriteLine( m.Field );
}
System.Console.In.ReadLine();
}
So in contrast to what I wrote above, I would recommend to NOT use a function to modify a struct within a using block. This seems wo work, but may stop to work in the future.
Mario
This behavior is undefined. In The C# Programming language at the end of the C# 4.0 spec section 7.6.4 (Member Access) Peter Sestoft states:
The two bulleted points stating "if the field is readonly...then
the result is a value" have a slightly surprising effect when the
field has a struct type, and that struct type has a mutable field (not
a recommended combination--see other annotations on this point).
He provides an example. I created my own example which displays more detail below.
Then, he goes on to say:
Somewhat strangely, if instead s were a local variable of struct type
declared in a using statement, which also has the effect of making s
immutable, then s.SetX() updates s.x as expected.
Here we see one of the authors acknowledge that this behavior is inconsistent. Per section 7.6.4, readonly fields are treated as values and do not change (copies change). Because section 8.13 tells us using statements treat resources as read-only:
the resource variable is read-only in the embedded statement,
resources in using statements should behave like readonly fields. Per the rules of 7.6.4 we should be dealing with a value not a variable. But surprisingly, the original value of the resource does change as demonstrated in this example:
//Sections relate to C# 4.0 spec
class Test
{
readonly S readonlyS = new S();
static void Main()
{
Test test = new Test();
test.readonlyS.SetX();//valid we are incrementing the value of a copy of readonlyS. This is per the rules defined in 7.6.4
Console.WriteLine(test.readonlyS.x);//outputs 0 because readonlyS is a value not a variable
//test.readonlyS.x = 0;//invalid
using (S s = new S())
{
s.SetX();//valid, changes the original value.
Console.WriteLine(s.x);//Surprisingly...outputs 2. Although S is supposed to be a readonly field...the behavior diverges.
//s.x = 0;//invalid
}
}
}
struct S : IDisposable
{
public int x;
public void SetX()
{
x = 2;
}
public void Dispose()
{
}
}
The situation is bizarre. Bottom line, avoid creating readonly mutable fields.

Why does my performance slow to a crawl I move methods into a base class?

I'm writing different implementations of immutable binary trees in C#, and I wanted my trees to inherit some common methods from a base class.
Unfortunately, classes which derive from the base class are abysmally slow. Non-derived classes perform adequately. Here are two nearly identical implementations of an AVL tree to demonstrate:
AvlTree: http://pastebin.com/V4WWUAyT
DerivedAvlTree: http://pastebin.com/PussQDmN
The two trees have the exact same code, but I've moved the DerivedAvlTree.Insert method in base class. Here's a test app:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Juliet.Collections.Immutable;
namespace ConsoleApplication1
{
class Program
{
const int VALUE_COUNT = 5000;
static void Main(string[] args)
{
var avlTreeTimes = TimeIt(TestAvlTree);
var derivedAvlTreeTimes = TimeIt(TestDerivedAvlTree);
Console.WriteLine("avlTreeTimes: {0}, derivedAvlTreeTimes: {1}", avlTreeTimes, derivedAvlTreeTimes);
}
static double TimeIt(Func<int, int> f)
{
var seeds = new int[] { 314159265, 271828183, 231406926, 141421356, 161803399, 266514414, 15485867, 122949829, 198491329, 42 };
var times = new List<double>();
foreach (int seed in seeds)
{
var sw = Stopwatch.StartNew();
f(seed);
sw.Stop();
times.Add(sw.Elapsed.TotalMilliseconds);
}
// throwing away top and bottom results
times.Sort();
times.RemoveAt(0);
times.RemoveAt(times.Count - 1);
return times.Average();
}
static int TestAvlTree(int seed)
{
var rnd = new System.Random(seed);
var avlTree = AvlTree<double>.Create((x, y) => x.CompareTo(y));
for (int i = 0; i < VALUE_COUNT; i++)
{
avlTree = avlTree.Insert(rnd.NextDouble());
}
return avlTree.Count;
}
static int TestDerivedAvlTree(int seed)
{
var rnd = new System.Random(seed);
var avlTree2 = DerivedAvlTree<double>.Create((x, y) => x.CompareTo(y));
for (int i = 0; i < VALUE_COUNT; i++)
{
avlTree2 = avlTree2.Insert(rnd.NextDouble());
}
return avlTree2.Count;
}
}
}
AvlTree: inserts 5000 items in 121 ms
DerivedAvlTree: inserts 5000 items in 2182 ms
My profiler indicates that the program spends an inordinate amount of time in BaseBinaryTree.Insert. Anyone whose interested can see the EQATEC log file I've created with the code above (you'll need EQATEC profiler to make sense of file).
I really want to use a common base class for all of my binary trees, but I can't do that if performance will suffer.
What causes my DerivedAvlTree to perform so badly, and what can I do to fix it?
Note - there's now a "clean" solution here, so skip to the final edit if you only want a version that runs fast and don't care about all of the detective work.
It doesn't seem to be the difference between direct and virtual calls that's causing the slowdown. It's something to do with those delegates; I can't quite explain specifically what it is, but a look at the generated IL is showing a lot of cached delegates which I think might not be getting used in the base class version. But the IL itself doesn't seem to be significantly different between the two versions, which leads me to believe that the jitter itself is partly responsible.
Take a look at this refactoring, which cuts the running time by about 60%:
public virtual TreeType Insert(T value)
{
Func<TreeType, T, TreeType, TreeType> nodeFunc = (l, x, r) =>
{
int compare = this.Comparer(value, x);
if (compare < 0) { return CreateNode(l.Insert(value), x, r); }
else if (compare > 0) { return CreateNode(l, x, r.Insert(value)); }
return Self();
};
return Insert<TreeType>(value, nodeFunc);
}
private TreeType Insert<U>(T value,
Func<TreeType, T, TreeType, TreeType> nodeFunc)
{
return this.Match<TreeType>(
() => CreateNode(Self(), value, Self()),
nodeFunc);
}
This should (and apparently does) ensure that the insertion delegate is only being created once per insert - it's not getting created on each recursion. On my machine it cuts the running time from 350 ms to 120 ms (by contrast, the single-class version runs in about 30 ms, so this is still nowhere near where it should be).
But here's where it gets even weirder - after trying the above refactoring, I figured, hmm, maybe it's still slow because I only did half the work. So I tried materializing the first delegate as well:
public virtual TreeType Insert(T value)
{
Func<TreeType> nilFunc = () => CreateNode(Self(), value, Self());
Func<TreeType, T, TreeType, TreeType> nodeFunc = (l, x, r) =>
{
int compare = this.Comparer(value, x);
if (compare < 0) { return CreateNode(l.Insert(value), x, r); }
else if (compare > 0) { return CreateNode(l, x, r.Insert(value)); }
return Self();
};
return Insert<TreeType>(value, nilFunc, nodeFunc);
}
private TreeType Insert<U>(T value, Func<TreeType> nilFunc,
Func<TreeType, T, TreeType, TreeType> nodeFunc)
{
return this.Match<TreeType>(nilFunc, nodeFunc);
}
And guess what... this made it slower again! With this version, on my machine, it took a little over 250 ms on this run.
This defies all logical explanations that might relate the issue to the compiled bytecode, which is why I suspect that the jitter is in on this conspiracy. I think the first "optimization" above might be (WARNING - speculation ahead) allowing that insertion delegate to be inlined - it's a known fact that the jitter can't inline virtual calls - but there's still something else that's not being inlined and that's where I'm presently stumped.
My next step would be to selectively disable inlining on certain methods via the MethodImplAttribute and see what effect that has on the runtime - that would help to prove or disprove this theory.
I know this isn't a complete answer but hopefully it at least gives you something to work with, and maybe some further experimentation with this decomposition can produce results that are close in performance to the original version.
Edit: Hah, right after I submitted this I stumbled on another optimization. If you add this method to the base class:
private TreeType CreateNilNode(T value)
{
return CreateNode(Self(), value, Self());
}
Now the running time drops to 38 ms here, just barely above the original version. This blows my mind, because nothing actually references this method! The private Insert<U> method is still identical to the very first code block in my answer. I was going to change the first argument to reference the CreateNilNode method, but I didn't have to. Either the jitter is seeing that the anonymous delegate is the same as the CreateNilNode method and sharing the body (probably inlining again), or... or, I don't know. This is the first instance I've ever witnessed where adding a private method and never calling it can speed up a program by a factor of 4.
You'll have to check this to make sure I haven't accidentally introduced any logic errors - pretty sure I haven't, the code is almost the same - but if it all checks out, then here you are, this runs almost as fast as the non-derived AvlTree.
FURTHER UPDATE
I was able to come up with a version of the base/derived combination that actually runs slightly faster than the single-class version. Took some coaxing, but it works!
What we need to do is create a dedicated inserter that can create all of the delegates just once, without needing to do any variable capturing. Instead, all of the state is stored in member fields. Put this inside the BaseBinaryTree class:
protected class Inserter
{
private TreeType tree;
private Func<TreeType> nilFunc;
private Func<TreeType, T, TreeType, TreeType> nodeFunc;
private T value;
public Inserter(T value)
{
this.nilFunc = () => CreateNode();
this.nodeFunc = (l, x, r) => PerformMatch(l, x, r);
this.value = value;
}
public TreeType Insert(TreeType parent)
{
this.tree = parent;
return tree.Match<TreeType>(nilFunc, nodeFunc);
}
private TreeType CreateNode()
{
return tree.CreateNode(tree, value, tree);
}
private TreeType PerformMatch(TreeType l, T x, TreeType r)
{
int compare = tree.Comparer(value, x);
if (compare < 0) { return tree.CreateNode(l.Insert(value, this), x, r); }
else if (compare > 0) { return tree.CreateNode(l, x, r.Insert(value, this)); }
return tree;
}
}
Yes, yes, I know, it's very un-functional using that mutable internal tree state, but remember that this isn't the tree itself, it's just a throwaway "runnable" instance. Nobody ever said that perf-opt was pretty! This is the only way to avoid creating a new Inserter for each recursive call, which would otherwise slow this down on account of all the new allocations of the Inserter and its internal delegates.
Now replace the insertion methods of the base class to this:
public TreeType Insert(T value)
{
return Insert(value, null);
}
protected virtual TreeType Insert(T value, Inserter inserter)
{
if (inserter == null)
{
inserter = new Inserter(value);
}
return inserter.Insert(Self());
}
I've made the public Insert method non-virtual; all of the real work is delegated to a protected method that takes (or creates its own) Inserter instance. Altering the derived class is simple enough, just replace the overridden Insert method with this:
protected override DerivedAvlTree<T> Insert(T value, Inserter inserter)
{
return base.Insert(value, inserter).Balance();
}
That's it. Now run this. It will take almost the exact same amount of time as the AvlTree, usually a few milliseconds less in a release build.
The slowdown is clearly due to some specific combination of virtual methods, anonymous methods and variable capturing that's somehow preventing the jitter from making an important optimization. I'm not so sure that it's inlining anymore, it might just be caching the delegates, but I think the only people who could really elaborate are the jitter folks themselves.
It's not anything to do with the derived class calling the original implementation and then also calling Balance, is it?
I think you'll probably need to look at the generated machine code to see what's different. All I can see from the source code is that you've changed a lot of static methods into virtual methods called polymorphically... in the first case the JIT knows exactly what method will be called and can do a direct call instruction, possibly even inline. But with a polymorphic call it has no choice but to do a v-table lookup and indirect call. That lookup represents a significant fraction of the work being done.
Life might get a little better if you call ((TreeType)this).Method() instead of this.Method(), but likely you can't remove the polymorphic call unless you also declare the overriding methods as sealed. And even then, you might pay the penalty of a runtime check on the this instance.
Putting your reusable code into generic static methods in the base class might help somewhat as well, but I think you're still going to be paying for polymorphic calls. C# generics just don't optimize as well as C++ templates.
You're running under the VS IDE, right? It's taking about 20 times longer, right?
Wrap a loop around it to iterate it 10 times, so the long version takes 20 seconds. Then while it is running, hit the "pause" button, and look at the call stack. You will see exactly what the problem is with 95% certainty. If you don't believe what you see, try it a few more times. Why does it work? Here's the long explanation, and here's the short one.

Can parameters be constant?

I'm looking for the C# equivalent of Java's final. Does it exist?
Does C# have anything like the following:
public Foo(final int bar);
In the above example, bar is a read only variable and cannot be changed by Foo(). Is there any way to do this in C#?
For instance, maybe I have a long method that will be working with x, y, and z coordinates of some object (ints). I want to be absolutely certain that the function doesn't alter these values in any way, thereby corrupting the data. Thus, I would like to declare them readonly.
public Foo(int x, int y, int z) {
// do stuff
x++; // oops. This corrupts the data. Can this be caught at compile time?
// do more stuff, assuming x is still the original value.
}
Unfortunately you cannot do this in C#.
The const keyword can only be used for local variables and fields.
The readonly keyword can only be used on fields.
NOTE: The Java language also supports having final parameters to a method. This functionality is non-existent in C#.
from http://www.25hoursaday.com/CsharpVsJava.html
EDIT (2019/08/13):
I'm throwing this in for visibility since this is accepted and highest on the list. It's now kind of possible with in parameters. See the answer below this one for details.
This is now possible in C# version 7.2:
You can use the in keyword in the method signature. MSDN documentation.
The in keyword should be added before specifying a method's argument.
Example, a valid method in C# 7.2:
public long Add(in long x, in long y)
{
return x + y;
}
While the following is not allowed:
public long Add(in long x, in long y)
{
x = 10; // It is not allowed to modify an in-argument.
return x + y;
}
Following error will be shown when trying to modify either x or y since they are marked with in:
Cannot assign to variable 'in long' because it is a readonly variable
Marking an argument with in means:
This method does not modify the value of the argument used as this parameter.
The answer: C# doesn't have the const functionality like C++.
I agree with Bennett Dill.
The const keyword is very useful. In the example, you used an int and people don't get your point. But, why if you parameter is an user huge and complex object that can't be changed inside that function? That's the use of const keyword: parameter can't change inside that method because [whatever reason here] that doesn't matters for that method. Const keyword is very powerful and I really miss it in C#.
Here's a short and sweet answer that will probably get a lot of down votes. I haven't read all of the posts and comments, so please forgive me if this has been previously suggested.
Why not take your parameters and pass them into an object that exposes them as immutable and then use that object in your method?
I realize this is probably a very obvious work around that has already been considered and the OP is trying to avoid doing this by asking this question, but I felt it should be here none-the-less...
Good luck :-)
I'll start with the int portion. int is a value type, and in .Net that means you really are dealing with a copy. It's a really weird design constraint to tell a method "You can have a copy of this value. It's your copy, not mine; I'll never see it again. But you can't change the copy." It's implicit in the method call that copying this value is okay, otherwise we couldn't have safely called the method. If the method needs the original, leave it to the implementer to make a copy to save it. Either give the method the value or do not give the method the value. Don't go all wishy-washy in between.
Let's move on to reference types. Now it gets a little confusing. Do you mean a constant reference, where the reference itself cannot be changed, or a completely locked, unchangeable object? If the former, references in .Net by default are passed by value. That is, you get a copy of the reference. So we have essentially the same situation as for value types. If the implementor will need the original reference they can keep it themselves.
That just leaves us with constant (locked/immutable) object. This might seem okay from a runtime perspective, but how is the compiler to enforce it? Since properties and methods can all have side effects, you'd essentially be limited to read-only field access. Such an object isn't likely to be very interesting.
Create an interface for your class that has only readonly property accessors. Then have your parameter be of that interface rather than the class itself. Example:
public interface IExample
{
int ReadonlyValue { get; }
}
public class Example : IExample
{
public int Value { get; set; }
public int ReadonlyValue { get { return this.Value; } }
}
public void Foo(IExample example)
{
// Now only has access to the get accessors for the properties
}
For structs, create a generic const wrapper.
public struct Const<T>
{
public T Value { get; private set; }
public Const(T value)
{
this.Value = value;
}
}
public Foo(Const<float> X, Const<float> Y, Const<float> Z)
{
// Can only read these values
}
Its worth noting though, that its strange that you want to do what you're asking to do regarding structs, as the writer of the method you should expect to know whats going on in that method. It won't affect the values passed in to modify them within the method, so your only concern is making sure you behave yourself in the method you're writing. There comes a point where vigilance and clean code are the key, over enforcing const and other such rules.
I know this might be little late.
But for people that are still searching other ways for this, there might be another way around this limitation of C# standard.
We could write wrapper class ReadOnly<T> where T : struct.
With implicit conversion to base type T.
But only explicit conversion to wrapper<T> class.
Which will enforce compiler errors if developer tries implicit set to value of ReadOnly<T> type.
As I will demonstrate two possible uses below.
USAGE 1 required caller definition to change. This usage will have only use in testing for correctness of your "TestCalled" functions code. While on release level/builds you shouldn't use it. Since in large scale mathematical operations might overkill in conversions, and make your code slow. I wouldn't use it, but for demonstration purpose only I have posted it.
USAGE 2 which I would suggest, has Debug vs Release use demonstrated in TestCalled2 function. Also there would be no conversion in TestCaller function when using this approach, but it requires a little more of coding of TestCaller2 definitions using compiler conditioning. You can notice compiler errors in debug configuration, while on release configuration all code in TestCalled2 function will compile successfully.
using System;
using System.Collections.Generic;
public class ReadOnly<VT>
where VT : struct
{
private VT value;
public ReadOnly(VT value)
{
this.value = value;
}
public static implicit operator VT(ReadOnly<VT> rvalue)
{
return rvalue.value;
}
public static explicit operator ReadOnly<VT>(VT rvalue)
{
return new ReadOnly<VT>(rvalue);
}
}
public static class TestFunctionArguments
{
static void TestCall()
{
long a = 0;
// CALL USAGE 1.
// explicite cast must exist in call to this function
// and clearly states it will be readonly inside TestCalled function.
TestCalled(a); // invalid call, we must explicit cast to ReadOnly<T>
TestCalled((ReadOnly<long>)a); // explicit cast to ReadOnly<T>
// CALL USAGE 2.
// Debug vs Release call has no difference - no compiler errors
TestCalled2(a);
}
// ARG USAGE 1.
static void TestCalled(ReadOnly<long> a)
{
// invalid operations, compiler errors
a = 10L;
a += 2L;
a -= 2L;
a *= 2L;
a /= 2L;
a++;
a--;
// valid operations
long l;
l = a + 2;
l = a - 2;
l = a * 2;
l = a / 2;
l = a ^ 2;
l = a | 2;
l = a & 2;
l = a << 2;
l = a >> 2;
l = ~a;
}
// ARG USAGE 2.
#if DEBUG
static void TestCalled2(long a2_writable)
{
ReadOnly<long> a = new ReadOnly<long>(a2_writable);
#else
static void TestCalled2(long a)
{
#endif
// invalid operations
// compiler will have errors in debug configuration
// compiler will compile in release
a = 10L;
a += 2L;
a -= 2L;
a *= 2L;
a /= 2L;
a++;
a--;
// valid operations
// compiler will compile in both, debug and release configurations
long l;
l = a + 2;
l = a - 2;
l = a * 2;
l = a / 2;
l = a ^ 2;
l = a | 2;
l = a & 2;
l = a << 2;
l = a >> 2;
l = ~a;
}
}
If you often run into trouble like this then you should consider "apps hungarian". The good kind, as opposed to the bad kind. While this doesn't normally tries to express constant-ness of a method parameter (that's just too unusual), there is certainly nothing that stops you from tacking an extra "c" before the identifier name.
To all those aching to slam the downvote button now, please read the opinions of these luminaries on the topic:
Eric Lippert
Larry Osterman
Joel Spolsky
If struct is passed into a method, unless it's passed by ref, it will not be changed by the method it's passed into. So in that sense, yes.
Can you create a parameter whose value can't be assigned within the method or whose properties cannot be set while within the method? No. You cannot prevent the value from being assigned within the method, but you can prevent it's properties from being set by creating an immutable type.
The question isn't whether the parameter or it's properties can be assigned to within the method. The question is what it will be when the method exits.
The only time any outside data is going to be altered is if you pass a class in and change one of it's properties, or if you pass a value by using the ref keyword. The situation you've outlined does neither.
The recommended (well, by me) is to use an interface that provides read only access to the members. Remembering that if the "real" member is a reference type, then only provide access to an interface supporting read operations for that type -- recursing down the entire object hierarchy.

Categories