編寫(xiě)高質(zhì)量C#代碼必備技巧(上)

以下為《編寫(xiě)高質(zhì)量代碼:改善C#程序的157個(gè)建議》作者【陸陸敏技】的讀書(shū)總結(jié)捉捅,添加了筆者自己的理解或示例。

首先簡(jiǎn)述幾個(gè)概念:

FCL:(Framework Class Library)即Framework類庫(kù)习劫。
基元類型:.NET 中咆瘟,編譯器直接支持的數(shù)據(jù)類型稱為基元類型(primitive type).基元類型和.NET框架類型(FCL)中的類型有直接的映射關(guān)系,例如:在C#中诽里,int直接映射為System.Int32類型袒餐。
[編譯器]直接支持的類型。
sbyte / byte / short / ushort /int / uint / long / ulong / char / float / double / bool

友情提示:Linq配合Lambda谤狡,會(huì)讓你的Code簡(jiǎn)潔許多匿乃,這也是C#發(fā)展歷史上非常重要的升級(jí)之一 C# 3.0 版


  1. string str = "海瀾"+666.ToString();string str = "海瀾"+666; 效率高,少了一次666值類型的裝箱豌汇。string.Format和StringBuilder對(duì)于字符串的拼接效率更高幢炸。(string.Format方法在內(nèi)部使用StringBuilder進(jìn)行字符串的格式化)

  2. 類型轉(zhuǎn)換盡量使用FCL中自帶的轉(zhuǎn)換方式。例如:int.TryParse("123")

  3. as比is的效率高拒贱,as只需要做一次類型兼容和一次null檢查宛徊,null檢查要比類型兼容檢查快(因?yàn)閕s為true還要進(jìn)行as操作)。但是as操作符不能操作基元類型逻澳,需要通過(guò)is進(jìn)行判斷,例如: obj is int

  4. TryParse比Parse好闸天,主要好在兩點(diǎn):1.不會(huì)引發(fā)異常。2.轉(zhuǎn)換成功時(shí)TryParse比Parse性能有略微提升斜做。轉(zhuǎn)換失敗時(shí)苞氮,TryParse比Parse性能提升600倍左右。

  5. 對(duì)于這種int i =-1;-1代表未賦值的魔數(shù)(又稱魔法值)瓤逼,使用Nullable (可空類型)是一個(gè)不錯(cuò)的選擇笼吟。

  6. const 是天然的 static库物,編譯期常量,所以使用const 變量效率會(huì)高贷帮。readonly僅僅在構(gòu)造函數(shù)中可賦值或多次賦值戚揭。

  7. 將枚舉的默認(rèn)值設(shè)置為0.

  8. 避免給枚舉類型的元素提供顯式的值。例如 enum Week{Monday = 1,Tuesday = 2}

  9. 習(xí)慣重載運(yùn)算符 c = a + b;c= a.add(b);要好

  10. 創(chuàng)建對(duì)象時(shí)需要考慮是否實(shí)現(xiàn)比較器撵枢。不過(guò)筆者更喜歡一行Lambda配合Linq梭哈民晒。

  11. 區(qū)別對(duì)待==和Equals,或明確指出這是引用相等Object.ReferenceEquals

  12. 重寫(xiě)Equals時(shí)也要重寫(xiě)GetHashCode并確保Hash值相等锄禽,例如:(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "_" + this.IDCode).GetHashCode();潜必。因?yàn)殒I值對(duì)集合,是根據(jù)Key 的HashCode來(lái)查找Value沃但。詳細(xì)請(qǐng)見(jiàn):Object.GetHashCode 方法

  13. 為頻繁打印的類重寫(xiě)ToString刮便。

  14. 正確實(shí)現(xiàn)淺拷貝和深拷貝(序列化與反序列化)。

  15. 使用dynamic來(lái)簡(jiǎn)化反射實(shí)現(xiàn)

  16. 元素個(gè)數(shù)小且高頻訪問(wèn)的集合用數(shù)組绽慈,頻繁更改長(zhǎng)度的集合用List<T>

  17. 多數(shù)情況下使用foreach進(jìn)行循環(huán)遍歷,并且會(huì)自動(dòng)將代碼置入try-catch塊辈毯,若類型實(shí)現(xiàn)了IDispose接口坝疼,它會(huì)在循環(huán)結(jié)束后自動(dòng)調(diào)用Dispose方法。

  18. foreach不能代替for谆沃,因?yàn)閒oreach使用迭代器進(jìn)行集合遍歷钝凶,foreach在FCL提供的迭代器內(nèi)部維護(hù)了一個(gè)對(duì)集合版本的控
    制,任何增刪操作都會(huì)是版本號(hào)+1唁影,一旦在遍歷時(shí)MoveNext中檢測(cè)版本號(hào)有變化耕陷,就會(huì)拋出異常。

  19. 使用更有效的對(duì)象和集合初始化据沈。例如:var pTemp = from p in personList2 selectt new {p.Name,AgeScope = p.Age>20?"Old":"Young"};

  20. 使用泛型集合代替非泛型集合哟沫。例如:IList<T>代替ArrayList,泛型集合是在原有基礎(chǔ)之上的優(yōu)化锌介,這也是命名空間System.Collections(非泛型)和System.Collections.Generic(泛型)這種父子關(guān)系的原因嗜诀。

  21. 選擇正確的集合,每種集合都有他們的優(yōu)缺點(diǎn)孔祸,善用它們隆敢。詳見(jiàn):Unity 之?dāng)?shù)據(jù)集合解析

  22. 確保集合的線程安全。使用lock鎖或線程安全集合(如:System.Collections.Concurrent命名空間下的ConcurrentDictionary)是一個(gè)不錯(cuò)的選擇崔慧,當(dāng)然最好的解決方案就是沒(méi)有線程競(jìng)爭(zhēng)拂蝎。

  23. 是如果需要自定義集合類,繼承IList<T>比List<T>要好惶室,盡量使用面向接口編程温自。

  24. 迭代器應(yīng)該是只讀的玄货。

  25. 如果類型的屬性中有集合屬性,那么應(yīng)該保證屬性對(duì)象是由類型本身產(chǎn)生的捣作。例如: var school = new School(new List<Student>{...})比 school.setList(外部產(chǎn)生的集合)要好誉结,因?yàn)橥獠慨a(chǎn)生的集合不可控。

  26. 使用匿名類型存儲(chǔ)并配合Linq查詢券躁,會(huì)讓你的程序更加靈活惩坑。【前提與你協(xié)作的同事也能看懂】

  27. 在查詢中使用Lambda表達(dá)式也拜∫允妫【前提與你協(xié)作的同事也能看懂】

  28. 理解延遲求職和主動(dòng)求值之間的區(qū)別。延遲一切能延遲的慢哈。例如:一個(gè)類型中某個(gè)成員的初始化或者賦值蔓钟,只有在用到這個(gè)成員的時(shí)候,才進(jìn)行這些操作卵贱。

  29. 本地查詢用IEnumerabel<T>,數(shù)據(jù)庫(kù)查詢用IQueryable

  30. 使用Linq取代集合中的比較器和迭代器

  31. 在Linq查詢中避免不必要的迭代滥沫。例如:有時(shí)(from c in list where c.Age>=20 select c).First()比f(wàn)rom c in list where c.Age==20 select c要效率的多

  32. 總是優(yōu)先考慮泛型〖悖可以有效的避免裝箱拆箱或重復(fù)代碼兰绣。

  33. 避免在泛型類型中聲明靜態(tài)成員。例如:MyList<int>與 MyList< float>中都含有static int count编振;這很令人迷惑缀辩。

  34. 為泛型參數(shù)設(shè)定約束。 添加約束的泛型產(chǎn)生的作用會(huì)更大踪央,例如 where T:Component臀玄,所以T類型就具備了組件的性質(zhì)。

  35. 使用default為泛型類型變量指定初始值畅蹂。不用擔(dān)心返回是值類型還是引用類型的困擾健无。

  36. 使用FCL中的委托聲明。Action液斜、Funtion讓代碼看上去更整潔統(tǒng)一睬涧。

  37. 使用Lambda表達(dá)式代替方法和匿名方法。Action tempAction = ()=>{Debug.Log("菜鳥(niǎo)海瀾");}更整潔

  38. 小心閉包中的陷阱旗唁。例如:尤其是for循環(huán)中的局部i變量畦浓。

  39. 委托的實(shí)質(zhì)就是一個(gè)含有方法引用的類。

  40. 使用event關(guān)鍵字為委托施加保護(hù)

  41. 實(shí)現(xiàn)標(biāo)準(zhǔn)的事件模型检疫。public delegate void EvenHandler(object sender,EventArgs e)

  42. 使用泛型參數(shù)兼容泛型接口的不可變性讶请。如果按照如下示例調(diào)用TestFuction(tempSon);就會(huì)報(bào)錯(cuò):CS1503 C# 參數(shù) 1: 無(wú)法從“Base<Son>”轉(zhuǎn)換為“IBase<Father>”,換句話說(shuō):編輯器認(rèn)為IBase<Father>IBase<Father>沒(méi)有任何關(guān)系

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    void Start()
    {
        Base<Son> tempSon = new Base<Son>();

        TestFuction(tempSon);//報(bào)錯(cuò)

        TestFuctionGeneric(tempSon);//正確
    }

    public void TestFuction(IBase<Father> parameter)
    {
    }

    public void TestFuctionGeneric<T>(IBase<T> parameter)
    {
    }

    public Father GetFather()
    {
        return new Son();   //Son是Fatrher的子類
    }
}

public interface IBase<T>
{
    void Play();
}

public class Base<T> : IBase<T>
{
    public void Play()
    {

    }
}
public class Father { }
public class Son : Father { }

介紹幾個(gè)概念
協(xié)變:讓返回值類型返回比聲明的類型派生程度更大的類型,就是“協(xié)變”。例如:

    public Father GetFather()
    {
        return new Son();   //Son是Fatrher的子類
    }

實(shí)際上夺溢,只要泛型類型參數(shù)在一個(gè)接口聲明中不被用來(lái)作為方法的輸入?yún)?shù)论巍,我們都可姑且把它看成是“返回值”類型的。

  1. 讓接口中的泛型參數(shù)支持協(xié)變风响,也就是如果需要讓第42條技巧編譯通過(guò)嘉汰。需要為接口添加out關(guān)鍵字public interface IBase<out T>

  2. 理解委托中的協(xié)變 協(xié)變和逆變 (C#)

  3. 為泛型類型參數(shù)指定逆變 協(xié)變和逆變 (C#)

  4. 顯示釋放資源需繼承接口IDisposable。補(bǔ)充兩個(gè)概念状勤,托管資源: 由CLR管理分配和釋放的資源鞋怀,即從CLR里new出來(lái)的對(duì)象。非托管資源:不受CLR管理的對(duì)象持搜,如windows內(nèi)核對(duì)象密似,或者文件、套接字等葫盼。

  5. 即使提供了顯示釋放方法残腌,也應(yīng)該在終結(jié)器(析構(gòu)函數(shù))中提供隱式清理(防止忘記)

  6. Dispose方法應(yīng)允許被多次調(diào)用。因?yàn)槟銦o(wú)法保證調(diào)用者真的只會(huì)調(diào)用一次贫导。

  7. 在Dispose模式中應(yīng)提取一個(gè)受保護(hù)的虛方法抛猫,也就是在public void Dispose()中調(diào)用protected virtual void Dispose(bool disposing)

  8. 在Dispose模式中應(yīng)區(qū)別對(duì)待托管資源和非托管資源。也就是protected virtual void Dispose(bool disposing)如果是參數(shù)是True則清理托管和非托管資源孩灯,如果是False則只清理非托管資源闺金。托管資源由CLR自行清理。

  9. 具有可釋放字段的類型或擁有本機(jī)資源的類型應(yīng)該是可釋放的钱反。可以理解為匣距,一個(gè)類A中持有類B面哥,在類B中有需要釋放的非托管資源,所以在類A中的Dispose要有釋放類B中非托管資源的操作毅待。

  10. 及時(shí)釋放資源尚卫,避免不要的資源浪費(fèi)或資源爭(zhēng)用(FileStream)

  11. 必要時(shí)應(yīng)將不再使用的對(duì)象引用賦值null,局部變量設(shè)置null毫無(wú)意義尸红,因?yàn)闊o(wú)論是否設(shè)置為null它都會(huì)被回收吱涉。而靜態(tài)字段如果不設(shè)置為null,則無(wú)法被回收外里。

  12. 為無(wú)用字段標(biāo)注不可序列化怎爵。[NonSerialized]

  13. 利用定特性減少可序列化的字段。如:OnDeserializedAttribute盅蝗、OnDeserializingAttribute鳖链、OnSerializedAttribute、OnSerializingAttribute墩莫。

  14. 使用繼承ISerializable接口更靈活地控制序列化過(guò)程(完全客制化定制序列化方式)芙委。

  15. 實(shí)現(xiàn)ISerializable的子類型應(yīng)負(fù)責(zé)父類的序列化逞敷。

  16. 用拋出異常代替返回錯(cuò)誤代碼。也就是用catch Exception代替 return errorMsg;并且在catch塊中僅僅是發(fā)送異常灌侣,并不處理異常推捐。

  17. 不要在不恰當(dāng)?shù)膱?chǎng)合下引發(fā)異常。一般在以下三種情況下才引發(fā)異常侧啼,對(duì)于可控(系統(tǒng)資源仍可用牛柒,資源狀態(tài)可恢復(fù))的錯(cuò)誤,根據(jù)情況自行處理慨菱,不要引發(fā)異常焰络。

  • 第一類情況 如果運(yùn)行代碼后會(huì)造成內(nèi)存泄漏、資源不可用符喝,或者應(yīng)用程序狀態(tài)不可恢復(fù)闪彼,則引發(fā)異常。
  • 第二類情況 在捕獲異常的時(shí)候协饲,如果需要包裝一些更有用的信息畏腕,則引發(fā)異常
  • 第三類情況 如果底層異常在高層操作的上下文中沒(méi)有意義,則可以考慮捕獲這些底層異常茉稠,并引發(fā)新的有意義的異常
  1. 重新引發(fā)異常時(shí)使用 Inner Exception

  2. 避免在finally內(nèi)撰寫(xiě)無(wú)效代碼描馅。 補(bǔ)充說(shuō)明

  3. 避免嵌套異常,因?yàn)闀?huì)覆蓋掉原本有用的堆棧信息而线。

  4. 避免“吃掉”異常,這里的“吃掉”指的是需要捕獲有意義的異常铭污。

  5. 為循環(huán)增加Tester-Doer模式而不是將try-catch置于環(huán)內(nèi),Try-Parse 模式和Tester-Doer模式是兩種替代拋異常的優(yōu)化方式膀篮,起到優(yōu)化設(shè)計(jì)性能的作用嘹狞。

  6. 總是處理未捕獲的異常。未捕獲異常通常就是運(yùn)行時(shí)期的Bug誓竿,我們可以在AppDomain.CurrentDomain.UnhandledException的注冊(cè)事件方法CurrentDomain_UnhandledException中磅网,將未捕獲的異常信息記錄在日志中。UnhandledException提供的機(jī)制并不能阻止應(yīng)用程序終止筷屡,也就是說(shuō)涧偷,執(zhí)行CurrentDomain_UnhandledException方法后,應(yīng)用程序就會(huì)終止毙死。

  7. 正確捕獲多線程中的異常燎潮,例如:

  • 正確
            Thread t = new Thread((ThreadStart)delegate
            {
                try
                {
                    throw new Exception("多線程異常");
                }
                catch (Exception error)
                {
                    MessageBox.Show("工作線程異常:" + error.Message + Environment.NewLine + error.StackTrace);
                }
            });
            t.Start();
  • 錯(cuò)誤
            try
            {
                Thread t = new Thread((ThreadStart)delegate
                {
                    throw new Exception("多線程異常");
                });
                t.Start();
            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace);
            }
  1. 慎用自定義異常

  2. 從System.Exception或其他常見(jiàn)的基本異常中派生異常

  3. 應(yīng)使用finally避免資源泄漏

  4. 避免在調(diào)用棧較低的位置記錄異常

  5. 區(qū)分異步和多線程應(yīng)用場(chǎng)景

  6. 在線程同步中使用信號(hào)量

  7. 避免鎖定不恰當(dāng)?shù)耐?/p>

  8. 警惕線程的IsBackgroud

  9. 警惕線程不會(huì)立即啟動(dòng)

  10. 警惕線程的優(yōu)先級(jí)

  11. 正確停止線程

  12. 應(yīng)避免線程數(shù)量過(guò)多

  13. 使用ThreadPool或BackgroundWorker代替Thread

  14. 用Task代替ThreadPool

  15. 使用Parallel 簡(jiǎn)化同步狀態(tài)Task的使用

  16. Paralle簡(jiǎn)化但不等同于Task默認(rèn)行為

  17. 小心Parallel中的陷阱

  18. 使用PLINQ,LINQ最基本的功能就是對(duì)集合進(jìn)行遍歷查詢扼倘,并在此基礎(chǔ)上對(duì)元素進(jìn)行操作跟啤。仔細(xì)推敲會(huì)發(fā)現(xiàn),并行編程簡(jiǎn)直就是專門(mén)為這一類應(yīng)用準(zhǔn)備的。因此隅肥,微軟專門(mén)為L(zhǎng)INQ拓展了一個(gè)類ParallelEnumerable(該類型也在命名空間System.Linq中)竿奏,它所提供的擴(kuò)展方法會(huì)讓LINQ支持并行計(jì)算,這就是所謂的PLINQ腥放。

  19. Task中的異常處理泛啸。 詳細(xì)說(shuō)明

  20. Parallel中的異常處理

static void Main(string[] args)  
{  
    try  
    {  
        var parallelExceptions = new ConcurrentQueue<Exception>();  
        Parallel.For(0, 1, (i) =>
        {  
            try  
            {  
                throw new InvalidOperationException("并行任務(wù)中出現(xiàn)的異常");  
            }  
            catch (Exception e)  
            {  
                parallelExceptions.Enqueue(e);  
            }  
            if (parallelExceptions.Count > 0)  
                throw new AggregateException(parallelExceptions);  
        });  
    }  
    catch (AggregateException err)  
    {  
        foreach (Exception item in err.InnerExceptions)  
        {  
            Console.WriteLine("異常類型:{0}{1}來(lái)自:  
                {2}{3}異常內(nèi)容:{4}", item.InnerException.GetType(),  
                Environment.NewLine, item.InnerException.Source,  
                Environment.NewLine, item.InnerException.Message);  
        }  
    }  
    Console.WriteLine("主線程馬上結(jié)束");  
    Console.ReadKey();  
}
  1. 區(qū)分WPE和WinForm的線程模型(untiy可忽略)
  2. 并行并不總是速度更快
  3. 在并行方法體中謹(jǐn)慎使用鎖,因?yàn)橛捎阪i的存在秃症,系統(tǒng)的開(kāi)銷也增加了候址,同步帶來(lái)的線程上下文切換,使我們犧牲了CPU時(shí)間與空間性能

更多技巧詳見(jiàn) Unity 之如何寫(xiě)出強(qiáng)壯的代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末种柑,一起剝皮案震驚了整個(gè)濱河市岗仑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聚请,老刑警劉巖荠雕,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異驶赏,居然都是意外死亡炸卑,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)煤傍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)盖文,“玉大人,你說(shuō)我怎么就攤上這事蚯姆∥逍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵龄恋,是天一觀的道長(zhǎng)疙驾。 經(jīng)常有香客問(wèn)我,道長(zhǎng)篙挽,這世上最難降的妖魔是什么荆萤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任镊靴,我火速辦了婚禮铣卡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偏竟。我一直安慰自己煮落,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布踊谋。 她就那樣靜靜地躺著蝉仇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上轿衔,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天沉迹,我揣著相機(jī)與錄音,去河邊找鬼害驹。 笑死鞭呕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宛官。 我是一名探鬼主播葫松,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼底洗!你這毒婦竟也來(lái)了腋么?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亥揖,失蹤者是張志新(化名)和其女友劉穎珊擂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體徐块,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡未玻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胡控。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扳剿。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖昼激,靈堂內(nèi)的尸體忽然破棺而出庇绽,到底是詐尸還是另有隱情,我是刑警寧澤橙困,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布瞧掺,位于F島的核電站,受9級(jí)特大地震影響凡傅,放射性物質(zhì)發(fā)生泄漏辟狈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一夏跷、第九天 我趴在偏房一處隱蔽的房頂上張望哼转。 院中可真熱鬧,春花似錦槽华、人聲如沸壹蔓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)佣蓉。三九已至覆劈,卻和暖如春眶蕉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工智末, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卧须,地道東北人璧亮。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓乖订,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親夺巩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贞让,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容