当使用Microsoft.SqlServer.TransactSql.ScriptDom进行解析时,访问者的实现通常是这样的:
open Microsoft.SqlServer.TransactSql.ScriptDom
let assembly _ =
let refs = ResizeArray()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:CreateAssemblyStatement) = refs.Add(s)
}
每个这样的访问者占用6个LOC,而唯一的区别是类型提示'CreateAssemblyStatement'
所以我试了这个:
let visitor<'T> _ =
let refs = ResizeArray<'T>()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:'T) = refs.Add(s)
}
let assemblyVisitor = visitor<CreateAssemblyStatement>
let executeVisitor = visitor<ExecuteStatement>
编译器对非限制类型的抱怨:
error FS3213: The member 'Visit: 'T -> unit' matches multiple overloads of the same method.
Please restrict it to one of the following:
Visit: TSqlFragment -> unit
Visit: StatementList -> unit
...
添加类型约束没有帮助(TSqlFragment是基类):
let visitor<'T when 'T :> TSqlFragment> _ =
let refs = ResizeArray<'T>()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:'T) = refs.Add(s)
}
所以我认为这种方法是不可能的,有没有其他优雅的方法来提出这个类型提示?
不幸的是,这是不可能的-而且我想不出一个优雅的解决方案。
问题在于TSqlFramentVisitor
的Visit
方法不是通用的。这是一个重载的Visit
方法,有大量的重载(1031根据我的VS工具提示!)这些方法中的每一个在逻辑上都是不同的方法(它们恰好具有相同的名称,但它们也可以具有不同的名称)。
这意味着没有办法编译通用的visitor
函数——因为编译器在编译visitor
函数时需要决定覆盖哪个Visit
方法。不幸的是,它不能这样做——因为只有当它知道方法参数的类型(从1031个方法中选择一个)时,它才能决定这一点。
(另一个问题是,如果有人定义了一个继承自TSqlFragment
的新类,这可能需要覆盖一个甚至不存在的方法!)