Range Indexing is released in C# 8.0 and it doesn't support for dynamic, it cannot be translated to another code (in this case is text.SubString()
) which means cannot resolve at runtime. I encountered the same problem with dynamic
in Lambda Tuple C# 8.0 also.
You might check how this translation work on the right side below code.
public class MyClass {
public class RangeIndexer
{
public string MyString { get; set; }
public char this[int index] { get => MyString[index]; }
public string this[Range range] { get => MyString[range]; }
}
public void Main() {
string s = "asdf";
Console.WriteLine(s[1..3]); // Translate to text.SubString()
dynamic d = "asdf";
Console.WriteLine("Index: " + d[1]); // Address to this[int]
//Console.WriteLine("Range1: " + d[1..3]); // Cannot translate to text.SubString() => Crashed
Console.WriteLine("Range2: " + d.Substring(1, 2)); // Local method of string
Console.WriteLine("Range3: " + $"{d}"[1..3]); // Cast as string and translate like Range1
dynamic rangeIndexer = new RangeIndexer();
rangeIndexer.MyString = "asdf";
Console.WriteLine("Range4: " + rangeIndexer[1..3]); // Address to this[range]
}
}
The range indexing
is translated to substring()
by IDE during compilation so it's not actually implemented in string class
, therefore it's explained why only single indexing d[1]
worked because it's declared.

So in short, we have 2 options
Option 1: In fact, dynamic
is using Reflection technical to resolve and grab if it's in scope of the variable, method,.. that means no more translation codes will occur in Reflection. Thus, casting dynamic
to specific type would help IDE translate them basically.
Option 2: Object should implement like RangeIndexer
class to work as dynamic type which make sure the Reflection could grab it. But almost classic types don't support so it only works with your own model.