views:

57

answers:

3

By way of explanation, take this value type in C#:

struct ObjRef
{
    public object Value;
    public ObjRef(object value) { Value = value; }
}

I can imagine an object graph where there are two boxed instances of this type, each holding a reference to the other. This is what I mean by a reference-cycle with only value-types.

My question is whether or not such an object graph can ever be constructed in .NET. Conceptually, the construction, if it exists, would go like this:

object left = new ObjRef();
object right = new ObjRef(left);
left.Value = right;

but obviously, the last line there is not valid C#. Making the last line:

((ObjRef)left).Value = right;

does not achieve the result as the cast unboxes left and you end up mutating a copy. So at least in straight C#, it doesn't look like the construction is possible.

Does anybody know if the construction could be achieved using reflection, unsafe code, dynamic, IL code, or in any other manner? Or, can anyone show that the CLR effectively prevents such a reference-cycle?

Please note that I don't actually want to create such an object graph. Rather, the answer may affect the design of algorithms which work with object graphs, such as serialization/deserialization formatters.


EDIT

As Brian suggested, it is indeed possible to modify the boxed value without unboxing it, by casting it to an interface type instead of the value type. So given this code:

interface IObjRef
{
    IObjRef Value { get; set; }
}

struct ObjRef : IObjRef
{
    IObjRef value;
    public IObjRef Value { get { return value; } set { this.value = value; } }
    public ObjRef(IObjRef value) { this.value = value; }
}

then the reference-cycle I describe can be constructed like this:

IObjRef left = new ObjRef();
IObjRef right = new ObjRef(left);
left.Value = right;

Which basically leaves us with reason #72 why mutable value-types are evil.

A: 

Honestly, I have not tried it, but see if have the Value property be on an interface, and then using the interface as your box lets you mutate the boxed itself rather than a new copy.

I vaguely feel like it is possible, though I'm unsure why I think that. Helpful, huh?

Brian
+1  A: 

This is possible by using an interface and having the value type implement the interface and to refer to each other. This allows them to create a cycle through boxed values since the struct when used with the interfaced reference will be boxed.

Quick Sample

interface ICycle
{
    void SetOther(ICycle other);
}

struct Cycle : ICycle
{
    ICycle value;
    public void SetOther(ICycle other)
    {
        value = other;
    }
}

class Example
{
    static void CreateCycle()
    {
        ICycle left = new Cycle();   // Left is now boxed
        ICycle right = new Cycle();  // Right is now boxed
        left.SetOther(right);
        right.SetOther(left);  // Cycle
    }
}

I share Brian's question though about wondering what advantage this will give you.

JaredPar
It gives no advantage at all; as I stated in the question, I don't want to create this cycle. Also, I'd never write a mutable value-type. However, a serialization formatter working on arbitrary object graphs has to be aware of the possibility, and not enter an infinite loop by assuming that reference types always form trees and not cycles. It may also impact the order in which objects have to be deserialized.
Wesley Hill
A: 

I didn't know structures could implement interfaces. That seems really bizarre; what's it good for? Is the dislike for structures in general, or for structures with properties and methods which act upon them? It's too bad .net doesn't allow one to declare certain structure properties and methods as mutators, whose use on "ReadOnly" structures would be forbidden.

supercat
Re: interfaces and structures: it's good for the same reason that interfaces are useful on reference types -- to allow you to specify a programming interface that doesn't care about the underlying type. A value type could quite reasonably implement IComparable and IEquatable and allow collection classes to make assumptions about the type that they wouldn't otherwise be able to do.
siride
I see your point about iComparable and iEquatable. I guess the problem is that there's no way of flagging methods that mutate an object from those which don't. Not that that wouldn't allow people to "cheat", but it would avoid most accidental 'gotchas'.
supercat