C# has an "unsafe" mode in which real, nasty pointers are allowed, but this mode is rarely used except in some messy situations involving talking to old code. The C# language proper does not have pointers.
In C and C++, you can get the address of anything, including a local variable on the stack:
int x = 0; int *p = &x; /* get address of x */ *p = 5; /* change value of x */
Of course, when a function exits, all its local variables cease to exist. Any remaining pointers to them are now "dangling" and must not be used. If they ever are... well, who knows what could happen? Make no mistake, it's an exciting world of opportunities and I've already had plenty of it, thanks.
Naturally we're all very happy not to have such nonsense in C#. But in fact, thanks to anonymous delegates, we can sort of almost do the same thing anyway. But without the undefined behaviour.
Here's the Ptr<T> generic class, which is a pointer - or at least can be a pointer if you construct it the right way:
public class Ptr<T> { Func<T> getter; Action<T> setter; public Ptr(Func<T> g, Action<T> s) { getter = g; setter = s; } public T Deref { get { return getter(); } set { setter(value); } } }
And here's how to use it:
int x = 0; Ptr<int> p = new Ptr<int>(() => x, v => x = v); // &x p.Deref = 5; // *p = 5 Debug.Assert(p.Deref == 5); // *p == 5
In practice it's a good deal more flexible than a C/C++ pointer. When we construct it, we get to specify exactly how to get or set the value, so there are an unlimited number of possible behaviours, but if we want pointer-like behaviour, we just need to replicate the pattern shown above, providing a getter that maps no arguments onto the variable's value, and a setter than maps one argument onto the assignment operation.
Using the pointer to modify or obtain the pointed-to value is much simpler. Instead of putting an asterisk in front, we put .Deref on the end.
It's tempting to think we could add another constructor like this:
public Ptr(ref T x) { getter = () => x; setter = v => x = v; }
... which would allow this:
Ptr<int> p = new Ptr<int>(ref x);
But lambdas are not allowed to refer to ref parameters.
I find it interesting that, thanks to anonymous delegates, C# already contains the plumbing necessary to implement the & (address-of) operator. It could in theory be done as mere syntactic sugar:
Ptr<int> p = &x;
... which would expand to this:
Ptr<int> p = new Ptr<int>(() => x, v => x = v);
Nothing else is really needed.
(Although to be honest, in the year or so I've been using C# in earnest, I haven't ever needed to take the address of a local value-type.)
2 comments:
Very neat .... I like it :-)
This is one of the more interesting peices of code I've seen in a while. I'm going to take advantage of this so I can bypass C# not allowing me to use ref parameters inside methods with yield return.
So I can control bulk operations and create/destroy sessions.
Post a Comment