(轉)Go blog (The Laws of Reflection)翻譯

The Laws of Reflection
(https://blog.golang.org/laws-of-reflection)

簡介

??Reflection(反射)在計算機中表示程序能夠檢查自身結構的能力,尤其是類型绪穆。它是元編程的一種形式,也是最容易讓人迷惑的一部分碧库。
??本文中,我們將解釋Go語言中反射的運行機制桐愉。每個編程語言的反射模型不大相同,很多語言索性就不支持反射(C、C++)氧卧。由于本文是介紹Go語言的,所以當我們談到"反射"時,默認為是Go語言中的反射殷费。

閱讀建議

??本文中,我們將解釋Go語言中反射的運行機制肛走。每個編程語言的反射模型不大相同,很多語言索性就不支持反射(C焚志、C++)映凳。

??由于本文是介紹Go語言的胆筒,所以當我們談到“反射”時,默認為是Go語言中的反射诈豌。

??雖然Go語言沒有繼承的概念仆救,但為了便于理解,如果一個struct A 實現(xiàn)了 interface B的所有方法時矫渔,我們稱之為“繼承”彤蔽。

類型和接口

??反射建立在類型系統(tǒng)之上,因此我們從類型基礎知識說起庙洼。

??Go是靜態(tài)類型語言顿痪。每個變量都有且只有一個靜態(tài)類型,在編譯時就已經確定油够。比如 int蚁袭、float32、*MyType石咬、[]byte揩悄。 如果我們做出如下聲明:

type MyInt int

var i int
var j MyInt

??上面的代碼中,變量 i 的類型是 int鬼悠,j 的類型是 MyInt删性。 所以,盡管變量 i 和 j 具有共同的底層類型 int焕窝,但它們的靜態(tài)類型并不一樣蹬挺。不經過類型轉換直接相互賦值時,編譯器會報錯它掂。

package reflect

import (
    "fmt"
    "reflect"
)

func kind() {
    type MyInt int
    var i MyInt
    var j int
    i = 1
    j = 1

    fmt.Printf("i type is %v \n", reflect.ValueOf(i).Type())
    fmt.Printf("j type is %v \n", reflect.ValueOf(j).Type())
    fmt.Printf("i kind is %v \n", reflect.ValueOf(i).Kind())
    fmt.Printf("j kind is %v \n", reflect.ValueOf(j).Kind())

}
結果:---------------------------------
i type is reflect.MyInt 
j type is int 
i kind is int 
j kind is int

??關于類型汗侵,一個重要的分類是 接口類型(interface),每個接口類型都代表固定的方法集合。一個接口變量就可以存儲(或“指向”晰韵,接口變量類似于指針)任何類型的具體值发乔,只要這個值實現(xiàn)了該接口類型的所有方法。一組廣為人知的例子是 io.Reader 和 io.Writer雪猪, Reader 和 Writer 類型來源于io包栏尚,聲明如下:

// Reader is the interface that wraps the basic Read method.
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Writer is the interface that wraps the basic Write method.
type Writer interface {
    Write(p []byte) (n int, err error)
}

??任何實現(xiàn)了 Read(Write)方法的類型,我們都稱之為繼承了 io.Reader(io.Writer)接口只恨。換句話說译仗, 一個類型為 io.Reader 的變量 可以指向(接口變量類似于指針)任何類型的變量,只要這個類型實現(xiàn)了Read 方法:

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on

??要時刻牢記:不管變量 r 指向的具體值是什么官觅,它的類型永遠是 io.Reader纵菌。再重復一次:Go語言是靜態(tài)類型語言,變量 r 的靜態(tài)類型是 io.Reader休涤。

??一個非常非常重要的接口類型是空接口咱圆,即:

interface{}

??它代表一個空集,沒有任何方法。由于任何具體的值都有零或更多個方法,因此類型為interface{}的變量能夠存儲任何值功氨。

??有人說,Go的接口是動態(tài)類型的序苏。這個說法是錯的!接口變量也是靜態(tài)類型的,它永遠只有一個相同的靜態(tài)類型。如果在運行時它存儲的值發(fā)生了變化,這個值也必須滿足接口類型的方法集合捷凄。

??由于反射和接口兩者的關系很密切忱详,我們必須澄清這一點。

接口變量的表示(The representation of an interface)

??Russ Cox 在2009年寫了一篇文章介紹 Go中接口變量的表示形式跺涤,具體參考文章末尾的鏈接“Go語言接口的表示”匈睁。(https://research.swtch.com/interfaces)。這里我們不需要重復所有的細節(jié)桶错,只做一個簡單的總結软舌。

??Interface變量存儲一對值:賦給該變量的具體的值、值類型的描述符牛曹。更準確一點來說佛点,值就是實現(xiàn)該接口的底層數(shù)據(jù),類型是底層數(shù)據(jù)類型的描述黎比。舉個例子:

var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
  return nil, err
}
r = tty

??示意性的講超营,此時r就包含了(value, type)對,即(tty, os.File)阅虫。注意:類型 os.File 不僅僅實現(xiàn)了 Read 方法演闭。雖然接口變量只提供 Read 函數(shù)的調用權,但是底層的值包含了關于這個值的所有類型信息颓帝。所以我們能夠做這樣的類型轉換:

var w io.Writer
w = r.(io.Writer)

??上面代碼的第二行是一個類型斷言米碰,它斷定變量 r 內部的實際值也繼承了 io.Writer接口窝革,所以才能被賦值給 w。賦值之后吕座,w 就指向了 (tty, *os.File) 對虐译,和變量 r 指向的是同一個 (value, type) 對。不管底層具體值的方法集有多大吴趴,由于接口的靜態(tài)類型限制漆诽,接口變量只能調用特定的一些方法。

??我們繼續(xù)往下看:

var empty interface{}
empty = w

??這里的空接口變量 empty 也包含 (tty, *os.File) 對锣枝。這一點很容易理解:空接口變量可以存儲任何具體值以及該值的所有描述信息厢拭。

??細心的朋友可能會發(fā)現(xiàn),這里沒有使用類型斷言撇叁,因為變量 w 滿足 空接口的所有方法(傳說中的“無招勝有招”)供鸠。在前一個例子中,我們把一個具體值 從 io.Reader 轉換為 io.Writer 時陨闹,需要顯式的類型斷言楞捂,是因為 io.Writer 的方法集合 不是 io.Reader 的子集。

??另外需要注意的一點是正林,(value, type) 對中的 type 必須是 具體的類型(struct或基本類型)泡一,不能是 接口類型颤殴。 接口類型不能存儲接口變量觅廓。

??關于接口,我們就介紹到這里涵但,下面我們看看Go語言的反射三定律杈绸。

第一反射定律(The first law of reflection):反射可以將“接口類型變量”轉換為“反射類型對象”。

1.從接口值到反射對象的反射(Reflection goes from interface value to reflection object

??注:這里反射類型指reflect.Typereflect.Value

??從用法上來講,反射提供了一種機制,允許程序在運行時檢查接口變量內部存儲的(value,type)對矮瘟。在最開始,我們先了解下reflect包的兩種類型:Type和Value瞳脓。這兩種類型使訪問接口內的數(shù)據(jù)成為可能它們對應兩個簡單的方法,分別是reflect.TypeOfreflect.ValueOf,分別用來讀取接口變量的reflect.Typereflect.Value部分。當然,從reflect.Value也很容易獲取到reflect.Type澈侠。目前我們先將它們分開劫侧。

??首先,我們看下reflect.TypeOf:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
}

上面這段程序將會打印輸出:

type: float64

??你可能會疑惑:為什么沒看到接口?這段代碼看起來只是把一個 float64類型的變量 x 傳遞給 reflect.TypeOf哨啃,并沒有傳遞接口烧栋。事實上,接口就在那里拳球。查閱一下TypeOf 的文檔godoc reports审姓,你會發(fā)現(xiàn) reflect.TypeOf 的函數(shù)簽名里包含一個空接口:

// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type

??我們調用 reflect.TypeOf(x) 時,x 被存儲在一個空接口變量中被傳遞過去祝峻; 然后reflect.TypeOf 對空接口變量進行拆解魔吐,恢復其類型信息扎筒。

??函數(shù) reflect.ValueOf 也會對底層的值進行恢復(這里我們忽略細節(jié),只關注可執(zhí)行的代碼):

var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x))

上面這段程序將會打印輸出:

value: 3.4

??類型 reflect.Type 和 reflect.Value 都有很多方法酬姆,我們可以檢查和使用它們嗜桌。這里我們舉幾個例子。類型 reflect.Value 有一個方法 Type()轴踱,它會返回一個 reflect.Type 類型的對象症脂。Type和 Value都有一個名為 Kind 的方法,它會返回一個常量淫僻,表示底層數(shù)據(jù)的類型诱篷,常見值有:Uint、Float64雳灵、Slice等棕所。Value類型也有一些類似于Int、Float的方法悯辙,用來提取底層的數(shù)據(jù)琳省。Int方法用來提取 int64, Float方法用來提取 float64,參考下面的代碼:

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())

上面這段代碼會打印出:

type: float64
kind is float64: true
value: 3.4

??還有一些用來修改數(shù)據(jù)的方法躲撰,比如SetInt针贬、SetFloat,在討論它們之前拢蛋,我們要先理解“可修改性”(settability)桦他,這一特性會在“反射第三定律”中進行詳細說明。

??反射庫提供了很多值得列出來單獨討論的屬性谆棱。首先是介紹下Value 的 getter 和 setter 方法快压。為了保證API 的精簡,這兩個方法操作的是某一組類型范圍最大的那個垃瞧。比如蔫劣,處理任何含符號整型數(shù),都使用 int64个从。也就是說 Value 類型的Int 方法返回值為 int64類型脉幢,SetInt 方法接收的參數(shù)類型也是 int64 類型。實際使用時嗦锐,可能需要轉化為實際的類型:

var x uint8 = 'x'
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())                            // uint8.
fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
x = uint8(v.Uint())                // v.Uint returns a uint64.

源碼:

// Uint returns v's underlying value, as a uint64.
// It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
func (v Value) Uint() uint64 {
    k := v.kind()
    p := v.ptr
    switch k {
    case Uint:
        return uint64(*(*uint)(p))
    case Uint8:
        return uint64(*(*uint8)(p))
    case Uint16:
        return uint64(*(*uint16)(p))
    case Uint32:
        return uint64(*(*uint32)(p))
    case Uint64:
        return *(*uint64)(p)
    case Uintptr:
        return uint64(*(*uintptr)(p))
    }
    panic(&ValueError{"reflect.Value.Uint", v.kind()})
}

??第二個屬性是反射類型變量(reflection object)的 Kind 方法 會返回底層數(shù)據(jù)的類型嫌松,而不是靜態(tài)類型。如果一個反射類型對象包含一個用戶定義的整型數(shù)意推,看代碼:

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

??上面的代碼中豆瘫,雖然變量 v 的靜態(tài)類型是MyInt,不是 int菊值,Kind 方法仍然返回 reflect.Int外驱。換句話說育灸, Kind 方法不會像 Type 方法一樣區(qū)分 MyInt 和 int

反射第二定律:反射可以將“反射類型對象”轉換為“接口類型變量”。

??和物理學中的反射類似昵宇,Go語言中的反射也能創(chuàng)造自己反面類型的對象

??根據(jù)一個 reflect.Value 類型的變量磅崭,我們可以使用 Interface 方法恢復其接口類型的值。事實上瓦哎,這個方法會把 type 和 value 信息打包并填充到一個接口變量中砸喻,然后返回。其函數(shù)聲明如下:

// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}

然后蒋譬,我們可以通過斷言割岛,恢復底層的具體值:

y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)

??上面這段代碼會打印出一個 float64 類型的值,也就是 反射類型變量 v 所代表的值犯助。

??事實上癣漆,我們可以更好地利用這一特性。標準庫中的 fmt.Println 和 fmt.Printf 等函數(shù)都接收空接口變量作為參數(shù)惠爽,fmt 包內部會對接口變量進行拆包(前面的例子中,我們也做過類似的操作)婚肆。因此坐慰,fmt 包的打印函數(shù)在打印 reflect.Value 類型變量的數(shù)據(jù)時较性,只需要把 Interface 方法的結果傳給 格式化打印程序:

fmt.Println(v.Interface())

??你可能會問:問什么不直接打印 v ,比如 fmt.Println(v)讨越? 答案是 v 的類型是 reflect.Value永毅,我們需要的是它存儲的具體值。由于底層的值是一個 float64沼死,我們可以格式化打幼胖稹:

fmt.Printf("value is %7.1e\n", v.Interface())

// Interface returns v's current value as an interface{}.
// It is equivalent to:
//  var i interface{} = (v's underlying value)
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i interface{}) {
    return valueInterface(v, true)
}

??上面代碼的打印結果是:

3.4e+00

??同樣,這次也不需要對 v.Interface() 的結果進行類型斷言意蛀∷时穑空接口值內部包含了具體值的類型信息县钥,Printf 函數(shù)會恢復類型信息

??簡單來說,Interface 方法和 ValueOf 函數(shù)作用恰好相反省有,唯一一點是,返回值的靜態(tài)類型是 interface{}蠢沿。

??我們重新表述一下:Go的反射機制可以將“接口類型的變量”轉換為“反射類型的對象”,然后再將“反射類型對象”轉換過去舷蟀。

反射第三定律:如果要修改“反射類型對象”,其值必須是“可寫的”(settable)扫步。

??這條定律很微妙匈子,也很容易讓人迷惑。但是如果你從第一條定律開始看旬牲,應該比較容易理解。

??下面這段代碼不能正常工作吭历,但是非常值得研究:

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

如果你運行這段代碼擂橘,它會拋出拋出一個奇怪的異常:

panic: reflect.Value.SetFloat using unaddressable value

??這里問題不在于值 7.1 不能被尋址,而是因為變量 v 是“不可寫的”通贞。“可寫性”是反射類型變量的一個屬性哭懈,但不是所有的反射類型變量都擁有這個屬性。

??我們可以通過 CanSet 方法檢查一個 reflect.Value 類型變量的“可寫性”茎用。對于上面的例子,可以這樣寫:

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())

上面這段代碼打印結果是:

settability of v: false

??對于一個不具有“可寫性”的 Value類型變量旭斥,調用 Set 方法會報出錯誤古涧。首先,我們要弄清楚什么“可寫性”羡滑。
??“可寫性”有些類似于尋址能力卒暂,但是更嚴格也祠。它是反射類型變量的一種屬性近速,賦予該變量修改底層存儲數(shù)據(jù)的能力∠鞔校“可寫性”最終是由一個事實決定的:反射對象是否存儲了原始值。舉個代碼例子:

var x float64 = 3.4
v := reflect.ValueOf(x)

??這里我們傳遞給 reflect.ValueOf 函數(shù)的是變量 x 的一個拷貝昔字,而非 x 本身首繁。想象一下,如果下面這行代碼能夠成功執(zhí)行:

v.SetFloat(7.1)

??答案是:如果這行代碼能夠成功執(zhí)行弦疮,它不會更新 x ,雖然看起來變量 v 是根據(jù) x 創(chuàng)建的咏尝。相反啸罢,它會更新 x 存在于 反射對象 v 內部的一個拷貝,而變量 x 本身完全不受影響扰才。這會造成迷惑,并且沒有任何意義累驮,所以是不合法的舵揭≡晡“可寫性”就是為了避免這個問題而設計的。

??這看起來很詭異映之,事實上并非如此蜡坊,而且類似的情況很常見赎败。考慮下面這行代碼:

f(x)

??上面的代碼中据忘,我們把變量 x 的一個拷貝傳遞給函數(shù)搞糕,因此不期望它會改變 x 的值。如果期望函數(shù) f 能夠修改變量 x汉规,我們必須傳遞 x 的地址(即指向 x 的指針)給函數(shù) f驹吮,如下:

f(&x)

??你應該很熟悉這行代碼,反射的工作機制是一樣的碟狞。如果你想通過反射修改變量 x,就咬吧想要修改的變量的指針傳遞給 反射庫射亏。
??首先竭业,像通常一樣初始化變量 x,然后創(chuàng)建一個指向它的 反射對象未辆,名字為 p:

var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())

??這段代碼的輸出是:

type of p: *float64
settability of p: false

??反射對象 p 是不可寫的咐柜,但是我們也不像修改 p,事實上我們要修改的是 *p拙友。為了得到 p 指向的數(shù)據(jù),可以調用 Value 類型的 Elem 方法辐棒。Elem 方法能夠對指針進行“解引用”,然后將結果存儲到反射 Value類型對象 v中:

v := p.Elem()
fmt.Println("settability of v:", v.CanSet())

??在上面這段代碼中漾根,變量 v 是一個可寫的反射對象辐怕,代碼輸出也驗證了這一點:

settability of v: true

??由于變量 v 代表 x, 因此我們可以使用 v.SetFloat 修改 x 的值:

v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)

??上面代碼的輸出如下:

7.1
7.1

??反射不太容易理解寄疏,reflect.Type 和 reflect.Value 會混淆正在執(zhí)行的程序,但是它做的事情正是編程語言做的事情妖泄。你只需要記姿也摺:只要反射對象要修改它們表示的對象,就必須獲取它們表示的對象的地址罚渐。

結構體(struct)

??在前面的例子中驯妄,變量 v 本身并不是指針,它只是從指針衍生而來青扔。把反射應用到結構體時,常用的方式是 使用反射修改一個結構體的某些字段谈息。只要擁有結構體的地址凛剥,我們就可以修改它的字段。

??下面通過一個簡單的例子對結構體類型變量 t 進行分析逻炊。

??首先犁享,我們創(chuàng)建了反射類型對象,它包含一個結構體的指針饼疙,因為后續(xù)會修改慕爬。

??然后屏积,我們設置 typeOfT 為它的類型磅甩,并遍歷所有的字段。

??注意:我們從 struct 類型提取出每個字段的名字渣聚,但是每個字段本身也是常規(guī)的 reflect.Value 對象僧叉。

type T struct {
    A int
    B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i,
        typeOfT.Field(i).Name, f.Type(), f.Interface())
}

上面這段代碼的輸出如下:

0: A int = 23
1: B string = skidoo

??這里還有一點需要指出:變量 T 的字段都是首字母大寫的(暴露到外部),因為struct中只有暴露到外部的字段才是“可寫的”隘道。
??由于變量 s 包含一個“可寫的”反射對象郎笆,我們可以修改結構體的字段:

s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)

??上面代碼的輸出如下:

t is now {77 Sunset Strip}

??如果變量 s 是通過 t ,而不是 &t 創(chuàng)建的激捏,調用 SetInt 和 SetString 將會失敗凄吏,因為 t 的字段不是“可寫的”。

結論

??最后再次重復一遍反射三定律:

    1. 反射可以將“接口類型變量”轉換為“反射類型對象”痕钢。
    1. 反射可以將“反射類型對象”轉換為“接口類型變量”盖喷。
    1. 如果要修改“反射類型對象”,其值必須是“可寫的”(settable)课梳。

??一旦你理解了這些定律,使用反射將會是一件非常簡單的事情跨算。它是一件強大的工具椭懊,使用時務必謹慎使用,更不要濫用。

??關于反射坏瘩,我們還有很多內容沒有討論漠魏,包括基于管道的發(fā)送和接收、內存分配哪自、使用slice和map禁熏、調用方法和函數(shù),由于本文已經非常長了瞧毙,這些話題在后續(xù)的文章中介紹

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末升筏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子您访,更是在濱河造成了極大的恐慌,老刑警劉巖灵汪,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件享言,死亡現(xiàn)場離奇詭異,居然都是意外死亡览露,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門命锄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偏化,“玉大人,你說我怎么就攤上這事驶冒。” “怎么了骗污?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵身堡,是天一觀的道長拍鲤。 經常有香客問我,道長擅这,這世上最難降的妖魔是什么景鼠? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮铛漓,結果婚禮上浓恶,老公的妹妹穿的比我還像新娘。我一直安慰自己包晰,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布勉痴。 她就那樣靜靜地躺著树肃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雏掠。 梳的紋絲不亂的頭發(fā)上筛谚,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音蚊伞,去河邊找鬼。 笑死时迫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的癞揉。 我是一名探鬼主播溺欧,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姐刁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了聂使?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弃理,失蹤者是張志新(化名)和其女友劉穎屎蜓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體控汉,經...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡返吻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年测僵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捍靠。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡榨婆,死狀恐怖,靈堂內的尸體忽然破棺而出良风,到底是詐尸還是另有隱情闷供,我是刑警寧澤统诺,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布粮呢,位于F島的核電站婿失,受9級特大地震影響啄寡,放射性物質發(fā)生泄漏。R本人自食惡果不足惜舟误,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一姻乓、第九天 我趴在偏房一處隱蔽的房頂上張望眯牧。 院中可真熱鬧,春花似錦剪个、人聲如沸版确。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吓蘑。三九已至,卻和暖如春磨镶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伟叛。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工脐嫂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侄榴,地道東北人网沾。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像辉哥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子醋旦,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容

  • 2018年12月9日饲齐,重慶成為上海《區(qū)塊鏈與產業(yè)創(chuàng)新》新書發(fā)布會后的第一站御雕,燁鏈科技來到重慶井通千鏈科技有限公司召...
    寒狼刺骨閱讀 341評論 0 3
  • 五月算是人間好時節(jié)滥搭,假若無閑事掛在心頭。 痛痛快快又不咸不淡地生活著闽坡,歡笑與失望并行愁溜,記憶里被塞上窗前明月光的明亮...
    _白棠閱讀 324評論 0 0