如何将子类传递给 proc 期望父类型的参数?

How can I pass subclass to proc expecting argument of parent type?

可以手动将 seq[Child] 转换为 seq[Parent],但也许有更好的选择?

type
  ParentRef = ref object of RootObj
    a: int

  ChildRef = ref object of ParentRef
    b: int
  
let parents = @[ParentRef()]
let children = @[ChildRef()]

proc process(list: seq[ParentRef]): void = discard list
process(parents)
process(children) # <== error

也许更好的选择是使用 generic proc?

type
  Parent = object of RootObj
    a: int
  Child = object of Parent
    b: int
  
let parents = @[Parent(a: 2)]
let children = @[Child(a: 3, b: 5)]

proc process[T: Parent](list: seq[T]): void =
  echo repr(list)
  echo "Accessing generic attribute a: ", list[0].a

process(parents)
process(children)

事实上,如果您不添加 T : Parent 限制,那么只要编译器在类型上找到它想要的所有字段,proc 就可以用于任何事情:

type
  Parent = object of RootObj
    a: int
  Child = object of Parent
    b: int

  FooBar = object of RootObj
    a: int
    bar: string
  
let parents = @[Parent(a: 2)]
let children = @[Child(a: 3, b: 5)]
let foobars = @[FooBar(a: 42, bar: "Yohoo")]

proc process[T](list: seq[T]): void =
  echo repr(list)
  echo "Accessing generic attribute a: ", list[0].a

process(parents)
process(children)
process(foobars)

Nim 的类型系统比很多都强大,默认情况下它只根据 these rules.

隐式转换类型

我们可以看到 sub-class 可以转换为其超类, 但是 seq[type1] 只能转换为 seq[type2] 如果 type1==type2,即它们是相同的,而不是子类型。

再添加一个隐式转换关系,定义一个type-converter,要么case by case:

converter seqChildToSeqParent(c:seq[ChildRef]):seq[ParentRef]= c.mapIt(it.ParentRef)

或一般用于任何子类型:

converter toSeqParent[T:ParentRef](x:seq[T]):seq[ParentRef]= x.mapIt(it.ParentRef)

定义其中一个转换器后,编译器将自动为您转换,调用 process(children) 将进行编译,并且 运行.