Nim教程【十三】(調查:你會順手幫博客園點個廣告嗎?)


類型轉換

Nim支持顯示類型轉換和隱式類型轉換

使用casts操作符完成顯示類型轉換工作,

顯示類型轉換工作是編譯期完成的工作,是位模式的

隱式類型轉換也是編譯期完成的工作,不是位模式的

讓我們來看一下下面的代碼

proc getID(x: Person): int =
  Student(x).id

在上面的代碼中

如果x變量的類型不是Student類型(Person類型是Student類型的父類型)

那么會拋出一個InvalidObjectConversionError異常。

對象變型

通常情況下,可變的對象類型實際意義更大一些

(譯注:這是面向對象三大要素之一“多態”的重要組成部分)

來看下面的代碼:

# This is an example how an abstract syntax tree could be modelled in Nim
type
  NodeKind = enum  # the different node types
    nkInt,          # a leaf with an integer value
    nkFloat,        # a leaf with a float value
    nkString,       # a leaf with a string value
    nkAdd,          # an addition
    nkSub,          # a subtraction
    nkIf            # an if statement
  Node = ref NodeObj
  NodeObj = object
    case kind: NodeKind  # the ``kind`` field is the discriminator
    of nkInt: intVal: int
    of nkFloat: floatVal: float
    of nkString: strVal: string
    of nkAdd, nkSub:
      leftOp, rightOp: PNode
    of nkIf:
      condition, thenPart, elsePart: PNode

var n = PNode(kind: nkFloat, floatVal: 1.0)
# the following statement raises an `FieldError` exception, because
# n.kind's value does not fit:
n.strVal = ""

從上面的代碼中可以看出

有繼承關系的對象之間做類型轉換非常簡單

訪問無效的對象屬性會引發一個異常

方法

對象的方法有兩個缺點

  • 程序員難以在運行期為一個類型增加方法(或者只能用丑陋的方法才能做到)

  • 很多時候方法的參數是不確定的

Nim為了避免類似的問題,不分配方法到類中

后面我們將了解Nim的動態綁定方法的特性

方法調用

可以使用點操作符調用對象的方法

obj.method(args)

而不是

method(obj,args)

如果沒有參數,則可以省略小括號

obj.method

方法調用是不受對象限制的,來看看下面的代碼:

echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper())
echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo")

我們再來看看面向對象的寫法

import strutils

stdout.writeln("Give a list of numbers (separated by spaces): ")
stdout.write(stdin.readLine.split.map(parseInt).max.`$`)
stdout.writeln(" is the maximum!")

是不是感覺很糟糕呢?

屬性

請看如下代碼來了解對象的屬性

type
  Socket* = ref object of RootObj
    FHost: int # cannot be accessed from the outside of the module
               # the `F` prefix is a convention to avoid clashes since
               # the accessors are named `host`

proc `host=`*(s: var Socket, value: int) {.inline.} =
  ## setter of hostAddr
  s.FHost = value

proc host*(s: Socket): int {.inline.} =
  ## getter of hostAddr
  s.FHost

var s: Socket
new s
s.host = 34  # same as `host=`(s, 34)

這個Socket類型,有一個host屬性,獲取這個屬性的值時

執行第二個方法,設置這個屬性的值時,執行第一個方法

(這個例子中也演示了inline方法)

我們可以在類型中重載方括號,以提供與數組相類似的屬性

請看如下代碼:

type
  Vector* = object
    x, y, z: float

proc `[]=`* (v: var Vector, i: int, value: float) =
  # setter
  case i
  of 0: v.x = value
  of 1: v.y = value
  of 2: v.z = value
  else: assert(false)

proc `[]`* (v: Vector, i: int): float =
  # getter
  case i
  of 0: result = v.x
  of 1: result = v.y
  of 2: result = v.z
  else: assert(false)

動態指派方法

需要使用method關鍵字來代替proc關鍵字

才能使用動態指派的特性

來看下面的代碼

type
  PExpr = ref object of RootObj ## abstract base class for an expression
  PLiteral = ref object of PExpr
    x: int
  PPlusExpr = ref object of PExpr
    a, b: PExpr

# watch out: 'eval' relies on dynamic binding
method eval(e: PExpr): int =
  # override this base method
  quit "to override!"

method eval(e: PLiteral): int = e.x
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)

proc newLit(x: int): PLiteral = PLiteral(x: x)
proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b)

echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))

再來看看下面的代碼

type
  Thing = ref object of RootObj
  Unit = ref object of Thing
    x: int

method collide(a, b: Thing) {.inline.} =
  quit "to override!"

method collide(a: Thing, b: Unit) {.inline.} =
  echo "1"

method collide(a: Unit, b: Thing) {.inline.} =
  echo "2"

var a, b: Unit
new a
new b
collide(a, b) # output: 2

因為決議是從左到右執行的

所以最后一個collide方法優於前面兩個collide方法

畢竟a和b都是Unit類型的

注意:Nim不產生虛方法表(C#.net是需要虛方法表的),

但是會生成調用樹(這樣做可以提升性能表現)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM