为什么范围索引在动力学上不起作用

时间:2020-05-19 02:09:01

标签: c# c#-8.0

我有一些代码,它抛出了。我不明白为什么。

string s="asdf";
Console.WriteLine(s[1..3]);

dynamic d="asdf";
Console.WriteLine(d[1..3]); // throws 
// RuntimeBinderException: The best overloaded method match for 'string.this[int]' has some invalid arguments

是否有一些静态解析的编译器魔术?生成的IL提示了这一点。

callvirt    System.String.Substring

是否可以在动态声明的表达式上使用范围索引?

2 个答案:

答案 0 :(得分:3)

Range Indexing在C#8.0中发布,它不支持动态,无法将其转换为其他代码(在这种情况下为text.SubString()),这意味着无法在运行时解析。在Lambda Tuple C#8.0中,dynamic也遇到了同样的问题。

您可以在代码下方的右侧选中how this translation work

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]
    }
}

range indexing在编译过程中被IDE转换为substring(),因此实际上并未在string class中实现,因此解释了为什么只声明单个索引d[1]的原因。

enter image description here

简而言之,我们有2个选择

选项1:实际上,dynamic正在使用Reflection技术来解析和抓取它是否在变量method ...的范围内,这意味着在Reflection中不会再出现任何翻译代码。因此,将dynamic转换为特定类型将基本上帮助IDE进行转换。

选项2:对象应像RangeIndexer类那样实现,以用作动态类型,以确保反射可以抓取它。但是几乎经典类型都不支持,因此只能与您自己的模型一起使用。

答案 1 :(得分:2)

扩展现有答案

有一些静态解决的编译器魔术吗?

是的,有。

具有范围(this[System.Range]this[start..end])的索引器受两种方式支持:显式和隐式。

明确 。如果类型声明了索引器this[System.Range],则每当我们使用符号this[start..end]时都将使用此索引器。编译器将表达式start..end转换为适当的System.Range对象,并生成调用索引器this[System.Range]的代码。

简单地 。如果type不包含索引器this[System.Range],则可以隐式支持范围索引。为了能够隐式支持范围索引,类型必须满足以下条件:

  • 它必须包含LengthCount属性。
  • 它必须包含带有两个Slice(start, end)参数的int方法。类型string是一种特殊情况;用于string类型的方法Substring代替Slice

如果类型满足这些条件,则当我们使用符号this[start..end]时,编译器会生成调用方法Slice(start, end)(或Substring(start, end)表示string)的代码。

有关更多详细信息,请参见C# specification: Ranges


因此string类型隐式支持范围索引。

考虑下一个代码示例:

dynamic d = "asdf";
Console.WriteLine(d[1..3]);

在编译期间无法确定dynamic是指支持范围索引的对象。因此,编译器不会生成调用方法Substring的代码。而是生成调用索引器this[System.Range]的代码。 string类型不包含此类索引器,因此将生成RuntimeBinderException