ARTICLE AD BOX
The proposal for allos ref struct doesn't explicitly cover how to call object methods.
However, looking at a similar generic method;
public string? Method<T>(T arg) where T : struct => arg.ToString();Results in the IL sequence constrained, callvirt. For the constrained opcode;
... when method was defined on Object, ValueType, or Enum and not overridden by thisType ... then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction
But;
You can't box a ref struct to ValueType or Object.
So this clearly isn't supported for a ref struct type. Which explains your implicit conversion error, even if this behaviour isn't explicitly documented anywhere else.
Instead you could require that all type arguments implement IFormattable;
public string? Method<T>(T arg) where T : IFormattable, allows ref struct => arg.ToString(null, CultureInfo.CurrentCulture);Which should be equivalent to how $"{arg}" would otherwise be compiled. If DefaultInterpolatedStringHandler supported ref struct at least.
Or introduce your own, similar interface, and force all callers to implement it.
4 Comments
Thanks Jeremy, this trick can be useful. Unfortunately in my case most of the time the T will be either string or ReadOnlySpan<char>, and neither of those types implements the IFormattable interface.
2026-02-18T05:20:08.337Z+00:00
I will assume that T overrides object.ToString, otherwise calling ToString must involve boxing, which you are not allowed to do to a ref struct.
One idea I thought of is to create a custom delegate with allows ref struct constraint like this:
delegate string StructToString<T>(ref T self) where T: allows ref struct;Then Delegate.CreateDelegate can be used to create such a delegate from the MethodInfo of the ToString method.
void Method<T>(T arg) where T: struct, allows ref struct { var toStringMethod = typeof(T).GetMethod("ToString")!; var del = (StructToString<T>)Delegate.CreateDelegate(typeof(StructToString<T>), toStringMethod); Console.WriteLine(del(ref arg)); }However, this only works if T is a value type. You would need a separate method with a different name for where T: class.
294k23 gold badges263 silver badges443 bronze badges
Explore related questions
See similar questions with these tags.

