Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] Casting in ref-returning methods (autogenerated getters and setters included) #31156

Open
Warpten opened this issue Nov 13, 2018 · 2 comments

Comments

@Warpten
Copy link

Warpten commented Nov 13, 2018

Version Used: 15.7.5

Steps to Reproduce:

I was messing around trying to wrap my head around ref-locals and ref-returns and I stumbled upon this roadblock. Consider the example below:

// Our base. Value is just there as an example, there could be methods.
interface Base {
    int Value {get;}
}

// An implementation of Base.
struct ValueType : Base {
    public int Value {get;}
}

class Obj {
    private ValueType _value;
    
    // (Obviously) happens with readonly too>
    public ref ValueType Value => ref _value; // Ok
    public ref Base Base => ref _value; // Does not compile
}

Or a little more contrived example so you can see the use case:

abstract class ObjBase {
    // Mutate **knows** Value is implemented, but does not know the actual type, and does not need to.
    public sealed void Mutate(int value) {
        Value.Value = value;
    }

    public abstract ref Base Value {get;}
}

sealed class Obj : ObjBase {
    private ValueType _value;
    
    public override ref Base Value => ref _value; // Won't compile
}

class Program
{
    static void Main(String[] args) {
        ObjBase o = new Obj();
        o.Mutate(5);
        Console.WriteLine(o.Value.Value); // Should print 5.
    }
}

(Please excuse the multiple editsI- it's night time)

Expected Behavior: I'm really not sure. Is this a limitation to the feature, or a design choice?

Actual Behavior: ref-returns and ref-locals do not allow downcasting the referenced object to one of its base classes or interfaces.

@Warpten Warpten changed the title [Question] Implicit casting in ref-returning methods (autogenerated getters and setters included) [Question] Casting in ref-returning methods (autogenerated getters and setters included) Nov 13, 2018
@gafter
Copy link
Member

gafter commented Nov 14, 2018

The expression ref _value is a reference to a value of type ValueType, not a reference to a value of type Base. To get the latter from the former you'd have to box it, and then it would no longer be a reference.

You can't even do this if ValueType were a class. If we allowed you to do this, you would be able to assign any Base to Obj.Base. But the variable it refers to can only hold a ValueType and not any other derived type of Base.

In short, what you're hoping to do would not be typesafe.

@Warpten
Copy link
Author

Warpten commented Nov 14, 2018

Ah, that makes sense, I hadn't considered this scenario. Anyways, coming back at it this morning, I realize now this can be worked around by declaring _value as Base and assigning default(ValueType) to it in Obj's constructor (or as an initializer):

public interface Base {
    int Property { get; }
}

public struct SuperA : Base {
    public int Property { get; }
}

public abstract class ObjBase {
    public abstract ref Base Value { get; }
}

public class Obj : ObjBase {
    private Base _property = default(SuperA); // Boxing happens here
    
    public override ref Base Value => ref _property;
}

This still boxes once, but now I'm able to obtain a reference to the member, as an interface, and mutate it (boo!), since boxing is done at the assignment site. It's not as pretty as I'd like to, but it works well enough. Neat!

However, what about making Value in ObjBase return a ref readonly? This ensures that the caller can no longer reassign the object returned by reference, my understanding being that ref-readonly is basically the C equivalent of T& const. Consider the following gist:

https://gist.github.com/Warpten/73feeee3f1686217ab49d80f5e389a67

However, I suppose this still runs into the "boxing ref-returns" situation when trying to make _property a member of type SuperA, and adding some obscure feature named "boxed reference returns" is probably not worth the effort for the use it would get.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants