这个拖后腿的“in”
問題之源
C# 7.2推出了全新的參數修飾符in,據說是能提升一定的性能,官方MSDN文檔描述是:
Add the?in?modifier to pass an argument by reference and declare your design intent to pass arguments by reference to avoid unnecessary copying.?
然而,想當然地使用它卻導致更多的副本出現,影響代碼運行速度。
MSDN中還有一段隱含的副作用的描述:
You can call any instance method that uses by value parameters. In those instances, a copy of the?in?parameter is created.?
同時文檔也提到了readonly ref的目的:
After adding support for?in?parameters and?ref redonly?[sic] returns the problem of defensive copying will get worse since readonly variables will become more common.
?來看一下MSDN里的這個例子::
private static double CalculateDistance(in Point3D point1, in Point3D point2)
{
? ? double xDifference = point1.X - point2.X;
? ? double yDifference = point1.Y - point2.Y;
? ? double zDifference = point1.Z - point2.Z;
? ? return Math.Sqrt(xDifference * xDifference + yDifference * yDifference + zDifference * zDifference);
}
假設Point3D類型是這樣定義的:
public struct Point3D
{
? ? public Point3D(double x, double y, double z)
? ? {
? ? ? ? X = x;
? ? ? ? Y = y;
? ? ? ? Z = z;
? ? }
? ? public double X { get; }
? ? public double Y { get; }
? ? public double Z { get; }
}
結果C#的幾個本不相關的特性以一種鬧心的方式結合起來:
標記了in的結構體參數是readonly只讀的
調用標記為readonly的結構體的實例化方法將產生一個副本
因為這個方法要通過改變this指針來達到確保標記了readonly的原值不會被修改
屬性訪問器也是實例方法,受this影響
每次給CalculateDistance方法傳遞標記了in的結構體參數時,編譯器會在訪問時自動為這個參數的每個屬性創建一個副本,本以為不會創建副本,結果反而每個傳進來的參數在方法內部弄出來3個!
這個問題存在已久,看一下Jon Skeet的博客:The Surprising Inefficiency of Readonly Fields。只不過使用in讓這個尷尬的場面更頻繁易現了。
解決方案
解決辦法同樣來自C# 7.2:readonly struct.
如果將public struct Point3D改成public readonly struct Point3D,因為所有字段也已經是readonly了,所以整個結構體都無需改變,編譯器此時也會省掉副本的操作,只有這樣才會出現結構體參數比按值傳遞獲得更快的運行速度。
不過注意在C# 7.1中結構體是可以通過標記ref來傳參達到同樣避免副本開銷的。盡管如此,結構體參數的字段想要改變值仍是可變的,甚至這個結構體都可以指向另一個新的。在函數的參數列表中使用in的聲明主要意圖還是為了告訴調用者本函數不會去修改傳進來的參數,當然編譯器也會配合強制保證。
示例
這里有一個關于in,?ref,?struct和readonly struct各種組合的性能測評(結構體size太小看不出差別,因此這個示例把結構體增加到56 bytes以便跑出更明顯的對比效果)。結果如下:
總結
當使用in代替ref表示設計意圖時,要明白在傳遞較大且較多的結構體時會有微小的性能損失
當使用in又要避免產生副本或提高性能,在聲明結構體時要使用readonly struct
原文地址?http://www.cnblogs.com/BeanHsiang/p/8687780.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的这个拖后腿的“in”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用.NET Core实现装饰模式和.NE
- 下一篇: RabbitMQ教程C#版 - 工作队列