日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

out参数不用赋值?这么神奇吗!

發布時間:2023/12/4 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 out参数不用赋值?这么神奇吗! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先提醒大家一下,docs.microsoft.com上的《C# 指南》是這樣描述out 參數修飾符[1]的:

作為 out 參數傳遞的變量在方法調用中傳遞之前不必進行初始化。但是,被調用的方法需要在返回之前賦一個值。

請注意上面加粗的話,然后看看下面的代碼片段,你覺得它能否編譯通過:

private?void?Test(out?System.Reflection.ParameterModifier?obj) {? //什么也不做 }

如果你很肯定地回答“不能”,那么恭喜你——答錯了。我當初看到這段代碼的第一感覺也是不能,但發現代碼確實能夠編譯通過。

分析原因

難道是語法改變了,官方文檔沒更新? 我又測試了一下:

private?void?Test2(out?string?obj)//編譯失敗 {? } private?void?Test3(out?int?obj)//編譯失敗 {? }

難道這個類型有什么特殊之處? 我把dotnet/runtime中的ParameterModifier源代碼[2]復制到本地項目,編譯同樣提示CS0177錯誤,WTF!!!

private?void?Test(out?ParameterModifier?obj) {? } public?readonly?struct?ParameterModifier {private?readonly?bool[]?_byRef;public?ParameterModifier(int?parameterCount){if?(parameterCount?<=?0)throw?new?ArgumentException();_byRef?=?new?bool[parameterCount];}public?bool?this[int?index]{get?=>?_byRef[index];set?=>?_byRef[index]?=?value;}#if?CORECLRinternal?bool[]?IsByRefArray?=>?_byRef; #endif }

深入Roslyn

應該是編譯器做了什么特殊處理!

于是我clone了dotnet/roslyn源代碼[3],本來想調試源代碼的,結果由于編譯時依賴包一直下載不下來,干脆直接讀源代碼了。

通過查找錯誤提示"must be assigned to before control leaves the current method",定位到CSharpResources.resx,確認錯誤編碼為ERR_ParamUnassigned:

??<data?name="ERR_ParamUnassigned"?xml:space="preserve"><value>The?out?parameter?'{0}'?must?be?assigned?to?before?control?leaves?the?current?method</value></data>

查找ERR_ParamUnassigned,定位到了編譯錯誤信息被添加的位置(DefiniteAssignment.cs文件內的ReportUnassignedOutParameter方法);

protected?virtual?void?ReportUnassignedOutParameter(ParameterSymbol?parameter,?SyntaxNode?node,?Location?location) {......if?(Diagnostics?!=?null?&&?this.State.Reachable){......if?(!reported){Debug.Assert(!parameter.IsThis);Diagnostics.Add(ErrorCode.ERR_ParamUnassigned,?location,?parameter.Name);}} }

因為同樣的方法定義,只是參數類型不一樣導致編譯報錯,因此猜測這個方法肯定進入了,只是this.State.Reachable值不同的原因,Reachable的代碼如下:

public?bool?Reachable {get{return?Assigned.Capacity?<=?0?||?!IsAssigned(0);} } public?bool?IsAssigned(int?slot) {return?/*(slot?==?-1)?||?*/Assigned[slot]; }public?void?Assign(int?slot) {if?(slot?==?-1)return;Assigned[slot]?=?true; }

繼續查找Assign的調用位置,發現一段很有意思的代碼:

Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type)); ...... state.Assign(slot);

IsEmptyStructType是不是意味著空Struct不檢查?立馬來試試:

private?void?Test(out?EmptyStruct?obj)///編譯通過 {? }public?struct?EmptyStruct {? }

繼續探究

但是ParameterModifier明顯不是空Struct,而且更奇怪的是為什么將源代碼復制到本地項目又不能編譯了。 帶著這個疑問,我們繼續深挖:

private?bool?IsEmptyStructType(TypeSymbol?type,?ConsList<NamedTypeSymbol>?typesWithMembersOfThisType) {......result?=?CheckStruct(typesWithMembersOfThisType,?nts);......return?result; }private?bool?CheckStruct(ConsList<NamedTypeSymbol>?typesWithMembersOfThisType,?NamedTypeSymbol?nts) {if?(!typesWithMembersOfThisType.ContainsReference(nts)){......return?CheckStructInstanceFields(typesWithMembersOfThisType,?nts);}return?true; } private?bool?CheckStructInstanceFields(ConsList<NamedTypeSymbol>?typesWithMembersOfThisType,?NamedTypeSymbol?type) {//?PERF:?we?get?members?of?the?OriginalDefinition?to?not?create?substituted?members/types?//???????unless?necessary.foreach?(var?member?in?type.OriginalDefinition.GetMembersUnordered()){if?(member.IsStatic){continue;}var?field?=?GetActualField(member,?type);if?((object)field?!=?null){var?actualFieldType?=?field.Type;if?(!IsEmptyStructType(actualFieldType,?typesWithMembersOfThisType)){return?false;}}}return?true; }

代碼檢查每個字段的類型是否是“空Struct”。這意味著如果所有實例字段都是“空Struct”,則原始類型也被視為“空Struct”,否則為“非空Struct”。看來關鍵就在GetActualField了:

private?FieldSymbol?GetActualField(Symbol?member,?NamedTypeSymbol?type) {switch?(member.Kind){case?SymbolKind.Field:var?field?=?(FieldSymbol)member;//?Do?not?report?virtual?tuple?fields.//?They?are?additional?aliases?to?the?fields?of?the?underlying?struct?or?nested?extensions.//?and?as?such?are?already?accounted?for?via?the?nonvirtual?fields.if?(field.IsVirtualTupleField){return?null;}return?(field.IsFixedSizeBuffer?||?ShouldIgnoreStructField(field,?field.Type))???null?:?field.AsMember(type);case?SymbolKind.Event:var?eventSymbol?=?(EventSymbol)member;return?(!eventSymbol.HasAssociatedField?||?ShouldIgnoreStructField(eventSymbol,?eventSymbol.Type))???null?:?eventSymbol.AssociatedField.AsMember(type);}return?null; }private?bool?ShouldIgnoreStructField(Symbol?member,?TypeSymbol?memberType) {return?_dev12CompilerCompatibility?&&?????????????????????????????//?when?we're?trying?to?be?compatible?with?the?native?compiler,?we?ignore((object)member.ContainingAssembly?!=?_sourceAssembly?||???//?imported?fieldsmember.ContainingModule.Ordinal?!=?0)?&&??????????????????????//?????(an?added?module?is?imported)IsIgnorableType(memberType)?&&?????????????????????????????????//?of?reference?type?(but?not?type?parameters,?looking?through?arrays)!IsAccessibleInAssembly(member,?_sourceAssembly);??????????//?that?are?inaccessible?to?our?assembly. }

必須是Struct和代碼不在同一個程序集(((object)member.ContainingAssembly != _sourceAssembly),字段類型必須是引用類型或數組(IsIgnorableType),并且是私有的(!IsAccessibleInAssembly)。我們來驗證一下,將ParameterModifier源代碼復制到類庫中:

//ConsoleApp1.csproj private?void?Test(out?ClassLibrary1.ParameterModifier?obj) { }//ClassLibrary1.csproj namespace?ClassLibrary1 {public?readonly?struct?ParameterModifier{private?readonly?bool[]?_byRef;?//編譯通過//private?readonly?string?_byRef;?//編譯通過//private?readonly?int?_byRef;?//編譯失敗//public?readonly?bool[]?_byRef;?//編譯失敗} }

結論

今天我們深入了編譯器的源代碼分析了一個簡單問題的成因:

一般來說,out參數必須在被調用方法將控制返回給調用方之前初始化。然而,編譯器可以進行優化,在某些情況下,如類型是沒有Public字段的Struct,將不會顯示編譯錯誤。

雖然感覺知道了也并沒什么鳥用,但至少說明了好的代碼風格還是非常重要的!希望這篇文章能夠對你有所啟發。

歡迎關注我的個人公眾號”My IO“

參考資料

[1]

out 參數修飾符: https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/out-parameter-modifier

[2]

ParameterModifier源代碼: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Reflection/ParameterModifier.cs

[3]

dotnet/roslyn源代碼: https://github.com/dotnet/roslyn

總結

以上是生活随笔為你收集整理的out参数不用赋值?这么神奇吗!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。