Repository navigation
ILens 11
- The new tool
search_codesearches the decompiled C# of a type, a namespace or the whole assembly for lines that match a regular expression, and lists each member that holds a match with its matching lines beneath it - The pattern is a .NET regular expression, case-sensitive and applied to one line at a time; signatures, attributes and field initializers are searched as well as method bodies, and a lambda's lines stand under the member it is written in
- The first search decompiles what it covers and keeps the text, so that later searches of the same assembly answer in under a second; on an assembly of 16 MB the first search over everything takes about 50 s
- A
search_codecall decompiles for at most 45 s and then answers how far it came, as inNot finished: 7945 of 9220 types decompiled. Repeat the call to continue; what is done is kept.; the new startup option--max-search-seconds <n>sets the limit - Generated types are searched only with
include_compiler_generated, or whentype_namenames one - The kept text takes at most a quarter of the memory budget; a search whose text does not fit ends in an error that points to
type_name,namespaceand--max-total-size - The server instructions name
search_code find_harmony_dependenciesnow lists the methods, properties and constructors of other assemblies' classes that a mod looks up by reflection, with the member that looks each up; such a line starts withMethod,PropertyorMemberand names the lookup call in brackets, as inMethod System.Text.StringBuilder.Append(string) <- Mod.Helper.Call [AccessToolsMethod]. A lookup of another assembly's static constructor made to call it, throughType.TypeInitializer, is listed as inMethod Shop.Store..cctor <- Mod.Hooks.Attach [TypeInitializer],Type.GetConstructorswith static binding flags andAccessTools.GetDeclaredConstructorswithsearchForStatictruename the static constructor, and a parameter that a mod pins as passed by reference, withMakeByRefType, readsref Tin such a line- A lookup whose result the list already shows as a patch target adds no line of its own, and neither does a lookup of a member of a class the scanned assembly defines
- A patch target a mod finds with
Type.GetConstructoror with one ofAccessTools' indexer lookups is now read, where the list showed? - The description of
find_harmony_dependenciesand the server instructions now say that the list includes these lookups find_harmony_dependenciesno longer lists(none) ? <- Namespace.Container [Attribute]for a class that carries[HarmonyPatch]but from which Harmony patches nothing, such as a container of nested patch classes, so a?again stands only for a target the scan could not read; over 391 library files of real mods, 20 such lines in 9 classes are gonefind_harmony_dependenciesnow lists a patch that Harmony never applies with the wordUnappliedin place of the patch kind, as inUnapplied Shop.Till.Open <- Patches.TillPatch.OpenPostfix [Attribute]: a method whose[HarmonyPatch]names a target but whose name is no patch type, such asOpenPostfix, and which carries no patch attribute, and a class whose own[HarmonyPatch]orTargetMethodnames a target but which has no patch method. Such a method showed nowhere, or only as a(none) ?line of its class, and such a class had(none)in place of the patch kind; over the same files, 3 methods in 6 files name a target that Harmony never patchesfind_harmony_dependenciesno longer prints the parameter types that an attribute gives a patch on a static constructor, a finalizer, or a getter, setter or event accessor named by its property or event, which Harmony ignores for these kinds: such a target now readsShop.Till..cctorwhere it readShop.Till..cctor(int), andShop.Till.set_Limitwhere it readShop.Till.set_Limit(string)for a setter that takes anint, so it names the method Harmony patches and can be passed on todecompileandanalyzeas it stands; an empty type list, which printed(), is left out the same wayfind_harmony_dependenciesnames a generic method a mod patches or looks up with the type arguments it is made for, as inPostfix Shop.Widgets.NumberField<int>, fromMakeGenericMethod,AccessTools.Method'sgenericsargument and aSymbolExtensions.GetMethodInfolambda, with?for one not read, where it named the method alone; over 386 library files of real mods, 13 lines in 7 files now name such type argumentsfind_harmony_dependencieswrites a parameter type named by text, in a patch's attribute that also gives aMethodTypeor throughType.GetType, as the other lines do:int[,]where it wroteInt32[,], and an array of a generic type such asList<int>[]where it wrote a broken name holding the assembly's qualifierfind_harmony_dependencieslists the events of other assemblies' classes that an assembly looks up by reflection withType.GetEvent, as lines that start withEvent, as inEvent Shop.Store.Opened <- Mod.Hooks.Attach [GetEvent], where it left them out, and an enumeration of a class's events withType.GetEventsasEvent Shop.Store.? <- Mod.Hooks.Attach [GetEvents]. Its description namesEventamong the first words of a line. Of the 381 assemblies of a game's installed mods, 8 files of 2 mods gain such a line, and no other answer changesfind_harmony_dependenciesnames an event's add or remove method that code takes right at the lookup, as inMethod Shop.Store.add_Opened <- Mod.Hooks.Attach [GetEvent], and lists a Harmony patch of such a method with the method as its target, as inPostfix Shop.Store.remove_Opened <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where the target was?find_harmony_dependenciesreads the lookups Harmony 2.4 adds toAccessTools. A patch whoseTargetMethodorHarmony.Patchcall finds its target withEventAdder,EventRemover,Finalizeror aDeclaredform of them, bytypeof, as text or in Harmony's extension spelling, names that target, as inPrefix Shop.Store.add_Opened <- Mod.Hooks.Before [TargetMethod]andPostfix Shop.Cache.Finalize <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where the target was?, and such a lookup made to call what it finds is listed, as inMethod Shop.Store.remove_Opened <- Mod.Hooks.Attach [EventRemover]andMethod Shop.Cache.Finalize <- Mod.Hooks.Attach [AccessToolsFinalizer], where it was left outfind_harmony_dependencieslists Harmony 2.4'sAccessTools.EventandDeclaredEventasEventlines, as inEvent Shop.Store.Opened <- Mod.Hooks.Attach [AccessToolsEvent], and names an add or remove method taken from them, in aMethodline and as a patch target, as it does for .NET'sType.GetEvent, where it left the lookup out and printed?for the targetfind_harmony_dependencieslists a method looked up withAccessTools.GetMethodByModuleAndTokenasMethod ? <- Mod.Hooks.Attach [GetMethodByModuleAndToken], where it left the lookup out; a module's ID and a metadata token name a method only in one build of that modulefind_harmony_dependenciesreads the lookups the release candidates of Harmony 3.0 add, of a method's lambdas, of a local function and of a state machine'sMoveNext, and names the method looked up with the kind after it, as inPrefix Shop.Store.Restock (Lambdas) <- Mod.Hooks.Before [TargetMethods],Shop.Store.Restock (LocalFunction Count)andShop.Store.Load (StateMachine), where the target was?. Of a game's installed mods, 24 library files are built against Harmony 2.4 and 2 against a release candidate of Harmony 3.0; none of them calls these lookups yet, and none of the 381 assemblies compared changes its answerfind_harmony_dependenciesnames a class's static constructor where a mod looks it up through .NET'sType.GetConstructorwith binding flags that holdStaticand notInstance, in each overload and also from a field, or throughType.TypeInitializer, as inPrefix Shop.Store..cctor <- Mod.Hooks.Before [TargetMethod]andPostfix Shop.Store..cctor <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply], where it printed?find_harmony_dependenciesreadsAccessTools'searchForStaticfrom a field or a parameter as well, where it read only a literaltrueand named the instance constructor otherwise, and names a lookup for the static constructor with parameter types with those types, as inShop.Store..cctor(int), a constructor no class has, since a static constructor takes none, where it named the static constructor, which such a lookup does not find- Where
find_harmony_dependenciescannot read a constructor lookup's flag, it names both constructors the lookup may find, as inPrefix Shop.Store..ctor()andPrefix Shop.Store..cctorwithDynamicTargetMethod, where it named the instance constructor alone forAccessTools' lookups and printed?forType.GetConstructor; with parameter types it names the instance constructor, the only one that takes them.AccessTools.allandallDeclared, which holdStaticandInstance, ask for an instance constructor. None of the 381 library files of a game's installed mods looks up a static constructor in any of these ways, and none of their answers changes find_harmony_dependencieslists the fields and methods a transpiler names to Harmony'sCodeInstructionby class and name, throughCall, also with"Namespace.Type:Method"text, and throughLoadFieldandStoreField, as inField Shop.Item.price <- Mod.Patches.Transpiler [LoadField]andMethod Shop.Store.Restock <- Mod.Patches.Transpiler [Call], where it listed none. A method of the scanned assembly's own class gives no line, as for the lookups listed before, and a field of it does. Of the 381 library files of a game's installed mods, 37 gain lines: 133 field loads, 27 field stores, 54 calls and 4 call matchesfind_harmony_dependencieslists the method a lambda names toCodeInstruction.Call,CodeMatch.Callsandnew CodeMatch, and the field a lambda names to Harmony 2.4'sSymbolExtensions.GetFieldInfoandCodeMatch'sLoadsFieldandStoresField, as inMethod Shop.Store.Count(string) <- Mod.Patches.Matches [Calls]andField Shop.Store.label <- Mod.Patches.Matches [LoadsField], where it listed none- A lambda that makes a delegate from a method group, as in
() => Helper, names the method, for those helpers and forSymbolExtensions.GetMethodInfo, as inPrefix Shop.Store.Count(string) <- Mod.Hooks.Before [TargetMethod], where it printedPrefix ? <- Mod.Hooks.Before [DynamicTargetMethod] - Where the decompiler writes such a lambda as the construction of its expression tree, as it does for a mod built against .NET 10 read beside a game's older System.Runtime,
find_harmony_dependenciesnames the same members, where aSymbolExtensions.GetMethodInfoline printed? find_harmony_dependencies' description says that it lists the fields and methods named in a transpilerfind_harmony_dependencieslists the method Harmony hands a prefix, postfix or finalizer as a delegate, where the patch method takes a parameter of a delegate type the mod marks with Harmony's[HarmonyDelegate]or[HarmonyPatch], as inMethod Shop.Store.Restock(int) <- Mod.Patches.Before [Injected], where it listed none, also where aHarmony.Patchcall names the patch method's class as text. A method of the scanned assembly's own class gives no line, as for the lookups listed before, and a marked delegate type that no patch method takes gives none, since Harmony then looks nothing up. Of the 381 library files of a game's installed mods, 2 gain such a line, a mod's library and a copy of itfind_harmony_dependencieslists the methodAccessTools.HarmonyDelegate<T>()looks up from the attributes of its delegate type, as inMethod Shop.Store.Restock <- Mod.Hooks.Attach [HarmonyDelegate], andMethod ? <- Mod.Hooks.Make [HarmonyDelegate]where that type is a type parameter, where it listed none- A delegate type marked with
[HarmonyPatch]no longer gives a line marked(none)infind_harmony_dependencies, since Harmony reads its attributes for the delegate alone find_harmony_dependencies' description says that it lists the methods a patch takes as delegate parametersfind_harmony_dependenciesprints a line once, however many lookups orHarmony.Patchcalls give it, where it printed the same line once for each of them, as for a reflection helper that tries four searches for every field it is given. Of the 381 library files of a game's installed mods, 34 answers lose 764 repeated lines, 657 of them in one mod's five builds- A line of
find_harmony_dependencieswith a?for a part it could not read ends with how often it was found, as inPostfix ? <- Mod.Hooks.After [PatchCall in Mod.Hooks.Apply] ×2, since what those calls name may differ; the?of a nullable value type, as inint?, is no such part find_harmony_dependencies' description says what?and×Nmeanfind_harmony_dependenciesanswers on Harmony's own library, which some mods ship beside their assembly, in 3 to 10 seconds; Release 10 took 16 to 25 seconds there and listed fewer linesfind_harmony_dependenciesnames the parameter type of a method a mod looks up in a class nested in a class it names as text, as inPostfix Shop.Outer.Part.Join(Part), where it printedJoin(?), and the same where the enclosing class comes fromtypeofbut its library is not loaded or lacks the nested class. No answer changes over the 381 library files of a game's installed mods and the 20 builds of Harmony's libraryfind_harmony_dependencieslists the fields a prefix, postfix or finalizer takes as___nameparameters where aHarmony.Patchcall names the patch method's class as text, throughAccessTools.TypeByNameorAccessTools.Method's"Type:Method"text, where it listed nonefind_harmony_dependencieswrites a nested class as a parameter type by its own name, as inJoin(Part)and..ctor(Part), where it wroteJoin(Outer.Part)for a class a mod names as text with+and..ctor(Outer.Part)for one a constructor patch's attribute namesfind_harmony_dependencieswrites a parameter of a patch target that a mod pins as passed by reference, withMakeByRefType, asref T, as inPrefix System.Int32.TryParse(string, ref int), where it wrote?. Harmony binds such a parameter whether the method declares itref,outorin, andref Tpasses back asparameter_typesto the other toolsfind_harmony_dependencieswrites a generic class a mod names by its reflection name without type arguments as ILens writes it everywhere else, in C#'s unbound form, as inPostfix Shop.Box<>.RunandSystem.Collections.Generic.Dictionary<,>.Enumerator.MoveNext, where it wroteShop.Box`1.RunandDictionary`2.Enumerator, so that the name passes back to the other tools; and as a parameter type by its own name. This holds on every route that reads a class from text,AccessTools.Field's"Type:field"andHarmonyPatch's string target among them, and for atypeofa patch attribute's bytes holdfind_harmony_dependencieskeeps a class nested in a generic class with type arguments where it reads the class from a patch attribute's bytes or from text, as a target and as a parameter type, as in..ctor(Dictionary<int, string>.Enumerator)where it wrote..ctor(Dictionary<int, string>), another type, andShop.Crate<int>.Slot<string>.Fillwhere it wroteShop.Crate<System.Int32, System.String>.Fill. No answer changes over the 381 library files of a game's installed mods and the 20 builds of Harmony's libraryfind_harmony_dependenciesrefuses a reference assembly the same way asanalyze, where it listed every patch class of a compiler's reference assembly as patching nothinganalyzewithread_byorassigned_byon a constant or an enum value now answers with an error saying that the compiler copies its value into each place that uses it, where it answeredNo readersorNo writerswhether or not anything used the constant; for an enum value the error points toused_byon the enum type- A decimal constant gets the same error, where
assigned_bynamed its static constructor as its one writer - A constant of a type from another assembly gets the same error once ILens reads it from the assembly that declares it; when no assembly ILens found defines the type, the empty answer about a member the inspected assembly does not reference ends with a line saying that a constant cannot be ruled out
analyzewithapplied_toon an attribute class whose base attribute class another assembly defines now lists where the attribute is applied, where it answered with an error saying the class does not derive fromSystem.Attributeapplied_toon a class whose base class ILens cannot load now answers the question, where it refused every such class; when nothing is marked with the class, the answer ends with a line saying it was not checked, as inNot checked: whether Mod.Helper is an attribute; its base class Lib.Base was not read, since ILens could not load Lib, Version=1.0.0.0analyzewithimplementsnow finds the members of interfaces that another assembly or the core library defines, where it answeredNo interface members implementedfor them; ILens reads them from the assemblies it can load beside the inspected oneimplementsnow also takes a property and an event, and an accessor such asget_Nameis answered with the interface's accessor, where it answered with nothing- When a base class or interface of the member's class cannot be loaded, the answer to
implementsends with a line naming it, for a member that can be bound through a base type, one that is public and not static or that overrides, as inNot checked: what Lib.IShape and its base types declare; ILens could not load Lib, Version=1.0.0.0 implementsno longer lists an interface member that another member of the class implements explicitly, one whose return type differs, or one the class has only through its base class while its member hides the base class's member instead of overriding it- The error for
implementson a member of a type from another assembly now says that it needs the class that declares the member, where it said that it reads the member's body analyzenow names the assembly or the module where an attribute sits on one of them, as inassembly Toolsormodule Tools.dll, whereused_byandapplied_toprintedICSharpCode.Decompiler.TypeSystem.MetadataModule; the line appeared for every assembly, since the compiler puts attributes on each- An attribute on the assembly and one on the module are now two lines, where both were the one line
used_byon a type the assembly defines now lists the assembly or the module when an attribute there names the type beside a value of another assembly's enum, or is given a value of the enum asked about, where both were left outused_byon a type from another assembly now finds an attribute that names it after a value of an enum the inspected assembly defines with an underlying type other thanint, passed as a named argument or as anobject, where the answer wasNo usersanalyzewithused_byon an enum the assembly defines now lists every type and member that carries an attribute given a value of it, where the answer named only the members whose signatures use the enumused_byon a type the assembly defines now lists a type or member whose attribute names it by atypeofinside an array, or as a type argument as intypeof(List<T>), where both were left outused_byon a type the assembly defines now lists a type or member whose attribute names it beside a value of an enum from another assembly, the framework's included, where such an attribute was skipped wholeused_byon an attribute class the assembly defines now lists every place the attribute is applied, also where it is given a value of an enum from another assembly, asapplied_todoesanalyzewithused_bynow reads an attribute that is given a value of another assembly's enum that does not take four bytes, such as abyteor alongenum, where such an attribute was not read at all: a type the attribute names bytypeofis answered with what carries the attribute, whether or not the enum's assembly can be loadedused_byon such an enum now lists everything that carries a value of it, as inassembly System.CoreforSystem.Security.SecurityRuleSet, where the answer wasNo usersor named only the members whose signatures use the enum; an enum given a value in the same attribute is answered in full as wellused_byon a type from another assembly now lists what carries an attribute class with type parameters that names it, when the attribute's constructor takes a value of a later type parameter and an earlier type argument is a constructed type such asList<int>; the answer left it out- Where an attribute's bytes can be read with two sizes of such an enum and only one reading names the type asked about, the answer does not list what carries it and ends with a line that starts
Not checked:, which names the carrier, the attribute class and the enum analyzewithused_byon a type now answers for an assembly that applies an attribute class with type parameters whose constructor takes a later type parameter after a type argument that is a class, a struct, an enum or a constructed type, as in[Ordered<List<int>, int>(5)], where one such attribute ended the question for every type of the assembly inAggregateException: One or more errors occurred. (Format of the executable (.exe) or library (.dll) is invalid.)analyzenow says when an answer is empty only because compiler-generated results were left out: under theNo ...line it counts them and names the setting that lists them, as inNot shown: 2 compiler-generated; include_compiler_generated lists them, whereNo implementations of System.Runtime.CompilerServices.IAsyncStateMachineread as if the assembly had no async methodanalyzeon a class or member the compiler generated, such as the class behind a lambda, now lists compiler-generated results withoutinclude_compiler_generated, aslist_members,find_methodsandsearch_codedo for such a class; who instantiates a lambda's class answeredNo symbols, where its static constructor doesanalyzewithused_byon a type no longer lists the type's own method, property or event as a user when its code sits in a lambda, an iterator or an async method; such a member was listed while the type's other members were left out, so a type nothing else uses could answer1 userused_byon a type withinclude_compiler_generatedno longer lists the fields and methods of the classes the compiler generated inside that type for its lambdas, iterators and async methodsanalyzenow reports a use under the method it is written in when it sits in a local function of a generic class or struct, in a lambda inside an iterator or async method of a generic class or a generic method, or in a lambda of a generic method that uses nothing butthis;used_by,read_by,assigned_byandinstantiated_byleft such a use out,call_siteslisted it under a generated name, anduseson the method left out what that code usesanalyzeno longer reports a use inside a lambda that captures nothing under another method of the same generic class; it named the first method of the class that has such a lambda, anduseslisted the symbols of all those lambdas for that method and none of them for the othersuseson a method of a generic class or on a generic method with a capturing lambda no longer listsSystem.Object..ctor(), and withinclude_compiler_generatedthe constructor of a closure class in a generic type is listed under its own name, both as for other typesanalyzewithusesnow writes a type parameter by its name where it printed a number: for a method of a generic class or a generic method it listsList<T>.Add(T)andShelf<T>._index, where it listedList<!0>.Add(!0)andShelf<!0>._indexusesno longer lists a member twice that a generic method and a lambda, iterator or async body inside it both use, such asCrate<!!0>.Make()besideCrate<!0>.Make(); the count above the list was one too high for each such memberuseson a property of a generic class whose getter calls its own setter no longer lists the property's own accessor, as for a class without type parametersanalyzewithusesnow lists a type the method casts to, tests for withisoras, creates an array of, or takes thedefaultof, where it listed a type only for atypeof; a method whose code did nothing else with the type answeredNo symbols used by ...usesnow lists a type also when the type's assembly does not lie beside the inspected one, by its name, as it lists the members of such a type; before, such a type got no line even for atypeof- The description of
analyzenow says thatusesdoes not list a type that is only a local variable's or stands in the signature of a member the method uses, and thatused_byon the type is the full test analyzewithuseson an async method now lists what the method's author wrote and leaves out what the compiler added to make it asynchronous: the calls on the task builder, the calls that ask an awaiter whether it is done and fetch its result, theGetAwaitercall anawaitcompiles to, and the awaiter's type; a method with oneawaitand three uses of its own listed about fifteen symbols, andinclude_compiler_generatedstill lists them alluseson an async method whose own code uses nothing now answersNo symbols used by ...with the lineNot shown: N compiler-generated; include_compiler_generated lists themunder itanalyzewithusesnow writes the keyword a called method declares for a parameter passed by reference,out,inorref readonly, when the method's library lies in another allowed folder and not beside the inspected assembly; it wrotereffor each, wheredecompileof the same method showedoutuseson a method that calls into a library ILens cannot read now ends with a line that says so:Not checked: whether ref on a method of Lib.Store is out, in or ref readonly; ILens could not load Lib, Version=1.0.0.0, or... Lib.Store as loaded declares no such methodwhen the library found is another version; before, the answer wroterefwith nothing to show that the keyword was unknownusesnow answers about seven times as fast on a large assembly: 4,387 questions took 8.5 s where Release 10 took 62 to 64 s, in two runs of each build on one machineanalyzewithused_byon an attribute's constructor now lists where the attribute is applied through that constructor, beside the code that calls it: the class or member that carries the attribute, the method for one on a parameter or a return value, and the assembly or the module; before, it listed calls in code alone and answeredNo usersfor a constructor that only attributes use, for an attribute class the assembly defines as for one of another assemblyused_byon such a constructor tells its overloads apart, whereapplied_toon the attribute class lists every place whatever the constructoranalyzewithcall_siteson an attribute's constructor now ends with a line that says how many attributes go through it,Not shown: 9 uses as an attribute; used_by lists them, also underNo calls to ...; before, the answer gave no sign that the constructor is used at all- The error for
call_siteson a member of another assembly's type now says thatused_bylists what uses the member, where it said the methods that call it analyzewithassigned_byon a field or property of an attribute class now lists where an attribute sets it by name, as in[Label(Text = "x")], beside the code that sets it: the class or member that carries the attribute, the method for one on a parameter or a return value, and the assembly or the module; before, it listed code alone and answeredNo writersfor a property only attributes setused_byon such a property and on its setter lists the same places, andcall_siteson the setter ends with a line that says how many attributes set the property,Not shown: 9 uses as an attribute; used_by lists them- The same holds for an attribute class of another assembly, where the answer said that the assembly references no such member; the line that names the members the assembly references now names those that attributes set by name
- An attribute of a class that inherits the field or property counts for the class that declares it; where ILens cannot load the library that says whether a class inherits it, the answer ends with a line that says so:
Not checked: whether Lib.SubAttribute inherits Kind from Lib.BaseAttribute; ILens could not load Lib, Version=1.0.0.0 analyzewithused_byon a method now lists what names the method itself, and no longer the code that uses only a base class's member the method hides. A class that declares a member of its base class anew, with or withoutnew, was answered with the users of both members: a getter declared anew in a subclass listed 25 users where 7 use it, and 19 answers on four assemblies named users where there is none- The same holds for
used_by,read_byandassigned_byon a property, an event or an indexer that hides one, which are answered through their accessors used_byon a method now answers for an assembly in which the code of a method breaks off inside an instruction, as tools that guard an assembly against decompiling write it; before, the question ended inBadImageFormatException: Read out of bounds.for every method of such an assembly.call_sitesnow lists a call that stands before such a break, where it left the calling method outused_byon a method, a property or an event now answers about nine times as fast as Release 10 on a large assembly: 1,511 questions about its methods took 82 s where Release 10 took 710 s, in one run of each build on one machineanalyzewithread_byorassigned_byno longer lists a method as one that may write a field when it passes the field byinorref readonlyto a virtual or interface methodanalyzewithused_byon a method of another assembly's class now names the method with the keyword of each parameter passed by reference,1 user of Lib.Store.TryGet(int, out string):where it wroteTryGet(int, string), and so do the lists of overloads in its errors; where ILens cannot read the method's declaration it writesref, and the answer ends withNot checked: whether ref on a method of Lib.Store is out, in or ref readonly; ILens could not load Lib, Version=1.0.0.0analyzewithusesnow writesreffor a read-only parameter of a virtual method whose library ILens cannot find, where it wrote the parameter as passed by value and listed two overloads that differ only in it as oneanalyzewithcall_sitesnow names each calling member once and lists its calls beneath it, each indented by two spaces, the formsearch_codelists its matches in:Shop.Ringers.Twice(Bell)on one line andbell.Ring(2)andbell.Ring()on the next two; before, each call stood on a line of its own that began with the member's full name,Shop.Ringers.Twice(Bell): bell.Ring(2). Withparameter_namethe arguments stand beneath the member in the same way.limitgoes on counting calls, and the header is unchanged- The list is smaller by this, 5 to 14 % summed over the calls to the methods of two assemblies and more for a method with many calls; an answer in which every member calls the method once grows by one character for each member
analyzewithcall_sitesnow lists a call that C# writes without call syntax, with what it passes, where it only counted it as not shown: an operator and a compound assignment (a + b,a += b), a conversion, written or not, the unwritten one as the cast that calls it ((Coin)5), the calls of aforeach(foreach (Coin item in till)), theAddcall behind an entry of a collection initializer ({ a }), the getter a property pattern calls ({ Empty: false }), theDeconstructcall of a deconstruction (var (count, empty) = till) and the base constructor call a constructor does not write (: base()). The header counts these calls,limitcuts them andparameter_nameshows their arguments like any others. An answer about one conversion operator of a large assembly was 3,154 lines that saidNot shownand 432,057 characters whatever the limit; it now starts7451 calls toand is 11,148 characters at the default limit- An initializer that fills what a property holds,
Spare = { a, b }, is now listed as a call of the property's getter for each entry - For a method that overrides, such a call counts as a call to the override when the type the pattern tests, the initializer fills, the
foreachruns over or the deconstruction takes apart is the override's class or derives from it - The calls that are still not read, the creation of a delegate, the
trueandfalseoperators of a condition and a property read through a pointer, now stand after the calls under one sentence with their sum and the members beneath it:Not shown: 3 calls in 2 members, which the decompiled C# does not show as a call:, thenShop.Rates.One()andShop.Rates.Two(): 2 of 2, a member's count written unless it is its one call. Before, every member had a line of its own with the whole sentence,Not shown: 2 of 2 calls in Shop.Rates.Two(), which the decompiled C# does not show as a call, andlimitdid not cut these lines.limitnow cuts the members of each such list on its own, and the sentence with the sum stays also with a limit of 0 - The members that failed to decompile and the members whose decompiled C# shows more calls than their IL holds take the same form:
Not shown: 1 call in 1 member, which failed to decompile:withShop.Counter.Reset() (InvalidCastException)beneath it, andCheck: 1 member shows more calls in the decompiled C# than its IL holds:withShop.Counter.Tally(): 2 where the IL holds 1 analyzewithused_bynow lists a method that calls its base class's method throughbase.inside a lambda that captures a variable, an iterator or an async method; such a method was missing from the list, and withinclude_compiler_generatedthe answer named a method the compiler adds for the call,Shop.Counter.<>n__0(), in its place. In one large assembly 445 such uses were missing, and one base method lacked 68 of its usersanalyzewithusesnow lists the base method for such a member, where it had no line for itanalyzewithcall_sitesnow lists such a call under the member it is written in,Shop.Counter.Open(int)withbase.Open(width)beneath it, once for each call written; it listed the call under the method the compiler adds, and for a member that also made the call outside the lambda it listed the call twice and ended with aCheck:line. The same for a call made through a local function that only passes it onused_byandusesanswer the same way for a base class another assembly defines, andused_byon the base class itself lists such a memberanalyzewithcall_siteson a method that overrides now lists a call the compiled code makes to that override directly once, where it listed the call and also ended with aNot shown:line and aCheck:line for the same call: a call throughbase.to an override the calling class does not override itself, a call of a generic method on an object of a class that overrides it, and a call on a value of a core library type such asintanalyzewithused_byon a virtual method now lists every member that calls the method on an object of a subclass where the compiled call names that subclass's override; such a member was missing from the list, and nothing said so. A C# compiler writes such a call for a method with a type parameter of its own,relay.Send(1)on an object of a class that overridesSend<T>, and a VB or an F# compiler for any virtual method, so that a method of an assembly from those compilers could answerNo userswhile it was called. A delegate made from the method on such an object counts in the same way. In one library written in VB, 116 of 21,624 answers about a method gained users, 668 lines in allanalyzewithcall_siteson such a method now lists those calls,8 calls to Net.Relay.Send(T):, where it listed only the calls made on an object of the class that declares the methodanalyzewithcall_siteson a method that overrides now lists a call made on an object of a class further down that overrides the method again, as the last line of the answer says it does; in code that names the override in the call, that call was missing. For a property's getter in such code the answer listed the call and also ended with aNot shown:line and aCheck:line for the same call- A call that names an override and that the decompiled C# does not show now stands under the sentence that names the declaration,
Not shown: 1 call to Net.Relay.Send(T) in 1 member, which the decompiled C# does not show as a call: - The line under an answer about a method that overrides now reads
Override: code can call Net.Relay.Send(T) instead, where it readcode calls: the list above the line can hold calls that name the override itself analyzewithoverridden_bynow lists an override with a more specific return type under every member it overrides; it was left out, so that an abstract method whose only override returns a more specific type answeredNo overridesanalyzewithused_byandcall_siteson a virtual member now lists the uses made on an object of a subclass that overrides the member with a more specific return type, where the compiled call names that subclass's override; those uses were missing, and nothing said so. The answers about such an override now end with the lineOverride: code can call Paper.Sheet.Copy() instead, as for any other overrideanalyzewithoverridden_byno longer lists an override of a member that a subclass declares anew withnew virtualunder the base class's member which that one hides and which it does not overrideanalyzewithoverridden_byon an accessor,get_Count, now lists the accessors that override it, where it answeredNo overridesanalyzewithused_byon an event that overrides now answers, with the line that names the event it overrides; the question never returned, and the server went on working on itanalyzewithimplemented_byon a member of an interface now lists the members that implement it, where it listed every member of a class below the interface that shares its name and parameters: a member that hides the implementing one, one with another return type, one that is not public, an ordinary member beside an explicit implementation, an override of any of those, and a member that a derived interface declares anew or makes abstract again. In 1,647 library files, 2,231 of 35,369 answers held such a lineimplemented_bynow lists a member that a class inherits from a base class without the interface, as inclass Stall : Counter, IShop { }whereCounterdeclaresOpen; it was missing, so that the answer could beNo implementationsfor a member every class implements that way. Such a line names the classes it holds for,Shop.Counter.Open() (for Shop.Kiosk, Shop.Stall, Shop.Stand and 2 more), and the member can belong to a class of another assembly. Where a base class could not be read, the answer ends with a line that startsNot checked:implemented_byon an accessor,get_Name, now lists the accessors that implement it, where it answeredNo implementationsimplemented_byon a member that belongs to no interface is now an error that names the class declaring it and, for a virtual member, points tooverridden_by; it answeredNo implementationsanalyzewithimplementsnow names the interface member for a member that a class below inherits and binds, with the classes it holds for,Shop.IShop.Open() (for Shop.Stall)- Both answers now count a member that implements through its metadata alone, whatever it is called: the
MoveNextof a class the compiler generates for an iterator or an async method, a member that implements under a name of its own in an assembly from the VB compiler, and the accessor methods the F# compiler writes for an interface's property, which the question about the property lists. In one VB-compiled library,implemented_byon an interface's property listed 33 properties that do not implement it and none from the 196 classes that do analyzewithimplemented_byno longer lists a member of a class under an interface's member when the class names only an interface derived from that interface and its base class implements the member already, andimplementsno longer names the interface member for it. A VB compiler writes such a class without a row for the base interface, and the runtime keeps the base class's implementation for it; the answers took the class's public member of the same name for the implementing one. For a class from a C# compiler, which names every base interface, nothing changesanalyzewithused_by,read_by,assigned_byandcall_siteson a member that implements an interface member now ends with a line that names the interface member code can call in its place,Implementation: code can call Shop.IShop.Open() instead; ask about that member for its uses.; the answer lists only what names the member itself, and nothing in it said that code reaches the member through the interface, so thatNo usersread as proof that a method such aspublic void Dispose()is never called. In seven libraries, 2,485 of 3,809 public members that implement an interface member answeredNo userswithout such a line- The line also stands for a member that implements through its metadata alone, whatever it is called: the
MoveNextof a class the compiler generates for an iterator or an async method, and a member that implements under a name of its own in an assembly from the VB compiler - The line replaces
Explicit implementation: code reaches it only through ...; ask about that member for its uses., which only an explicit implementation had; that line saidonlyalso under an answer that lists a user, as for the genericGetEnumeratorof an iterator's class, which the class's own code calls by name - A member that implements and also overrides now has both lines, the one that names the interface member before the line that starts
Override:; it had the second alone - Where ILens could not read a library that decides whether a member implements, these answers now end with
Not checked: whether it implements an interface member; ILens could not load Lib, Version=1.0.0.0 analyzewithextension_methodslists, for a type the assembly defines and for one of another assembly, the extension methods of every class whose receiver is the type, by value or by reference, where it left out those of a Visual Basic module and every receiver passed by reference, and for another assembly's type a receiver ofIntPtrorUIntPtr; over a game's 106 libraries, the .NET runtime and the SDK's Visual Basic libraries, 105 lists of a type the assembly defines and 192 of another assembly's type gain methodsanalyzewithextension_methodslists an extension method whose receiver is a type parameter under each type its constraints name, and one whose constraints name no type underSystem.ValueTypewhere it asks for a struct and underSystem.Objectotherwise, where no type listed itanalyze'susesnames the event where a method reads or writes the field that holds its subscribers, asShop.Bell.Changedfor a Visual Basic method that raises the event, where it named the fieldShop.Bell.ChangedEventanalyzeleaves the other fields a compiler adds out of its answers unlessinclude_compiler_generatedis set, as it leaves out an auto-property's backing field: inuses, a line that names the field of a Visual Basic auto-implemented property, a field F# adds or an older C# compiler's cached lambda, and inused_byon a delegate type, the field of a Visual Basic eventanalyze'sused_byon a Visual Basic event lists the methods that raise it, as it does for a C# event, where it listed only the code that subscribes, orNo usersfor an event only its own class raises. 5 events of .NET 10's Visual Basic library for Windows Forms gain the methods that raise themcall_siteswrites the default an omitted argument takes aslist_memberswrites it, asPace.Fast (default), where it wrote the enum's number, and its header withnon_default_onlynames that default, aspasses pace other than its default Pace.Fastanalyzeused_byon an enum's underlying type names the enum, asShop.Widthamong the users ofSystem.Byte, where it named the enum's field,Shop.Width.value__;exposed_byon that type no longer lists the fieldcall_sitesnames a parameter aslist_memberswrites it, as inargument for @switchandpasses @default other than its default 3, and so do its errors that name a method's parametersanalyzeanswers a question that reads code,used_by,instantiated_by,read_by,assigned_by,call_sitesoruses, asked of a reference assembly, such as a .NET reference pack, a .NET Framework reference assembly, a publicized copy of an assembly in a build folder or a compiler's reference assembly, with an error that says that the assembly holds a library's declarations without their code, to ask the build the library runs as, and which kinds read declarations alone and answer there, where it answered from the placeholder bodies that nothing uses, calls, creates, reads or writes the member. On a publicized copy of a game's main assembly,used_byon a logging method answeredNo userswhere the game's own build names 200compareno longer ends in the errorAn item with the same key has already been addedwhen a type has two members whose types print alike: classes of one name in two namespaces or in two enclosing classes, an array of one dimension beside one of two, a method's type parameter named like its class's, or properties with parameters that are not indexers, as VB declares them. Withouttype_namethe error ended the list for the whole assembly; 13 of 1,722 library files measured gave itcomparenow lists a member whose parameter, return or field type gave way to a type of the same name in another namespace or enclosing class, or to an array of another rank, as removed and added. It took the two for the same member and listed it as changed, or not at all where its body was unchanged: across 326 pairs of versions of real libraries 268 members changed that way, and 157 of them were not listedcompareno longer lists a method whose type parameter was renamed as removed and addedcomparepairs two members of one type that share their whole signature, which an obfuscator can leave, in the order they stand in the type, where it ended in that errorcompareasked for one method by name now says when the name finds different methods in A and in B, as the comparison of their type counts them, and names both:// Shop.Till.Open() → object in A and Shop.Till.Open() → string in B are different methods — bodies are identical in both assemblies.It named A's method alone, so that the answer read as unchanged for a method whose return type, parameter types or type parameters changed, or that moved to a base class or gave way to an extension method or an explicit implementation. Asked for each name that the comparison of a type lists as removed and added, in 18 pairs of versions of real libraries, 466 answers compared two different methods, 31 of them as identical- Where the two bodies differ, that line stands before them, also with
formatil, whose bodies do not show the methods' signatures compareasked for one method by name now says where it found a method the asked type does not declare,[inherited from Shop.Counter]or[extension method on Shop.Till], asdecompiledoescomparenow shows a constant or an enum member whose value changed between the two builds, as~ public const int Limit = 5 → 6, and names the kindvaluesin the list of changed types, as in~ Shop.Color [values]. It showed no such change, and a type whose constants alone changed was not listed: between two versions of a game's main assembly, 86 enum members and 42 constants changed value, most enum members moved up by a member inserted before them, of which the member list showed the added member alone, and 2 enums that were only renumbered were missing from the list. The compiler copies such a value into the code built against the library, which keeps the old one.comparewrites a constant's values aslist_membersdoes, as~ public const bool Open = false → true, and a constant of an enum type that names the same member after the enum's members were numbered anew is no change, as// Shop.Picks.Usual — Pace.Slow in both assemblies, while the new numbers show at the enum's own memberscompareasked for one constant or enum member by name now answers with its value in each build,// Shop.Limits.Size — 5 in A, 6 in B., where it answered that a field has no body. The name means the constant where the nearest class that has the name declares one, also where it hides a base class's method of that name, as an enum member namedEqualsdoes. The values of two constants of one name are written in full where their enums are two classes of one short namecompareasked for one method by name now answers two bodies that differ with a unified diff of them, the changed lines with three unchanged lines around each change under--- A: pathand+++ B: path, where it printed both bodies in full under// === A: path ===and// === B: path ===. Over 8,952 such answers from real version pairs, in C# and in IL, the answers came to 27 % of their characters, and an answer of over 10,000 characters to 3 to 17 % in the mediancomparetakes the parametercontext, the number of unchanged lines shown around each change; a number larger than the method shows it whole with each change marked, which is how a whole IL body stays reachablecomparewithformatilshows only the instructions that changed: it reads the IL without its offsets, so an instruction inserted early does not mark the instructions after it, whose offsets moved, and a jump that lands on another instruction is marked although its text without offsets is the same- A hunk inside the code the compiler generated for the method, such as a lambda or a local function, names that part after its
@@ compareasked for a field by name now names the constructors that set it in each build, as inShop.Till.Limit is a field, which has no body; Shop.Till..cctor() sets it., where it pointed to the instance constructor.ctorfor every field. A static field's initializer is in the static constructor, so that pointer led tonot foundon a static class, or to an answer about a constructor that holds none of the field's code, which readbodies are identicalwhere the initializer had changed; in real libraries 45 to 48 % of the fields a constructor initializes are static- Where no constructor sets the field, the answer says so and names the question that finds what does:
no constructor of Shop.Till sets it, and assigned_by lists what does. - Where the constructors that set the field differ between the builds, the answer names them for each build. It also says which build alone has a field, names a base class's constructors for a field a class inherits, and says that a constructor
may set itwhere it passes the field's address on to code ILens cannot follow compareasked for a field, a property or an event by name now answers for one that only the second build has, where it answeredMember 'Label' not found on Shop.Till, its base types, or as an extension method.although the type's member list showed it as added. Over 2,972 pairs of versions of real libraries, 11,729 of 11,865 such questions got that answer- A property or an event that only one build has is answered with its code, as a method that only one build has is:
// Shop.Till.Label — only B has it.and below it the property asdecompileshows it, or withformatilthe IL of each accessor under a line that names it. One that only the first build has pointed to its accessors, so the code took up to two more calls, and for an event declared without accessors of its own they showed the code the compiler generated, wheredecompileshows one line - A field that only the second build has is answered as one that only the first build has:
Shop.Till.Limit is a field, which has no body, and only B has it; Shop.Till..ctor() sets it. comparenow shows a member that keeps its name, parameter types and return type and whose declaration changed, such as a method madeprivate,staticorvirtual, a field madereadonlyor turned into a constant, a property whose setter becameinternalorinit, or a renamed parameter. Its line in a type's member list holds both declarations,~ private bool Check(int a) → internal static bool Check(int a), and the type list names the kinddeclarations, as in~ Shop.Till [declarations]. Before, such a member showed nowhere unless its code changed as well: over 2,993 pairs of versions of real libraries, 871 of 1,490 such members showed nowhere- A member whose declaration and method body both changed ends its line with
[body], and a property or an event names the accessors whose bodies changed, as for a body alone. Before, such a member was listed with the first build's declaration alone, which read as a change of its code only compareasked for one member by name now names both declarations first where they differ,// public void Open(int count) → private void Open(int count), before the verdict or the diff, in C# and in IL. It answeredbodies are identical in both assemblies.for a method madeprivateor one whose parameter was renamed, which breaks a Harmony patch that binds that parameter by name. An accessor is named with its property's or event's declaration, and a constant's values follow the linecompareasked for a field whose declaration changed now says so:Shop.Till.Limit is a field, which has no body, and its declaration changed: public int Limit → public readonly int Limit; Shop.Till..ctor() sets it.compareasked by name for a name that the two builds have as members of different kinds, such as a field, a method or a constant that became a property, a property that became a method, or a property that gave way to an extension method, now names both, as in// Shop.Till.Limit is a field in A and a property in B: public int Limit → public int Limit { get; set; }, and below that shows the code of each member that has code under a line that names its build,// B: Shop.Till.Limit, in C# or, withformatil, in IL. It described one build's member alone: a field that became a property answeredis a field, which has no body; an initializer it has is in the constructors, .ctor., a property that became a field or a constant pointed to accessors only the first build has, and a method that became a property, or the reverse, was shown as the one build's method underonly A has itoronly B has it. Over 3,028 pairs of versions of real libraries, 113 names changed their kindcompareasked by name for a property or an event that both builds have now names both declarations where they differ, and the accessors of both builds, marking each that only one build has, as inShop.Till.Depth is a property, and its declaration changed: public int Depth { get; } → public int Depth { get; set; }; compare its accessors, get_Depth, and set_Depth, which only B has.It named the first build's accessors alone and no declaration, so a setter the second build gained went unmentioned and a property madevirtualor given aninternalsetter read as unchanged; of 384 properties of real version pairs whose declaration changed, 383 now show it, and 130 of them gained or lost an accessorcompareasked by name for a field that is a constant in one build now says so for that build, as inin A Shop.Kinds..cctor() sets it, and in B it is a constant., where it said that an initializer it has is in the constructors,.ctorcompareasked by name for a field, a property or an event that both builds have under the name as two different members, such as a field or a property whose type changed, now says so and names both declarations, as inShop.Till.Limit is a field, which has no body, and A and B have different fields of that name: public int Limit → public long Limit; Shop.Till..ctor() sets it.andShop.Till.Depth is a property, and A and B have different properties of that name: public int Depth { get; } → public long Depth { get; set; }; compare its accessors, get_Depth, and set_Depth, which only B has.It answered as if both builds had one member, without a word that the type changed, although code built against the first build no longer finds such a member; over 3,154 pairs of versions of real libraries, 889 fields and 430 properties changed their type that way- The same holds for a field or a property that one build declares and the other inherits from a base class, such as a property the second build now overrides or a field that moved to the base class: a declaration found on the base class is marked with it, as in
public virtual string Label { get; } [inherited from Shop.Store] → public override string Label { get; }, and the answer names the type asked about, where it named the base class's member when the first build inherits it; over 3,056 of those pairs, 364 names changed that way comparereports a method that becomes an extension method or stops being one as a changed declaration, as in~ public static void Promote(Host host) → public static void Promote(this Host host), with its type listed underdeclarations, where it reported no change; between three versions of a game's main assembly, 3 methods became extension methodscompare, asked through another library's type where one build finds an extension method of the assembly and in the other the type has a method of that name, as when each build lies beside its own version of the library, saysonly A finds it through Lib.Kettle; in B the type has a member of that name, which C# takes firstcompare, asked through a type that meets the constraints of an extension method whose receiver is a type parameter in one build alone while both declare the method, says— only A finds it through Shop.Box; in B Shop.Box does not meet the method's constraints.comparenames the library another build takes a type from where one build defines the type and the other does not, as after the type moved to a library: the list of changed types writes- Shop.Kettle, which in B comes from Lib, Version=1.0.0.0, the comparison of the type3 member changes in Shop.Kettle, which only A has; in B it comes from Lib, Version=1.0.0.0:, and each answer that only one build has a member ends with; in B Shop.Kettle comes from Lib, Version=1.0.0.0, where all three read as if the type had been deleted. A class nested in such a type is named with the library of the class enclosing it. Between consecutive builds of the .NET SDK, of the .NET runtimes from 8.0 to 10.0 and of mod libraries, 453 types that one build defines are taken by the other from another assembly- Where the other build forwards the type to another assembly, the same three answers name that assembly, as
- Shop.Kettle, which B forwards to Lib, Version=1.0.0.0, where the list wrote- Shop.Kettle comparewithmember_nameanswers a method that one build declares on a type and the other, which takes the type from a library or forwards it there, declares as an extension method for that type with both methods and the comparison of their bodies, as// Shop.Kettle.Boil() → void in A and Shop.KettleExtensions.Boil(Kettle) → void [extension method on Shop.Kettle] in B are different methods — bodies are identical in both assemblies., where it answered// Shop.Kettle.Boil() → void — only A has it.Where ILens cannot load that library, the answer starts withNot checked: whether Shop.Kettle or its base types have a member Boil, which C# would take first; ILens could not load Lib, Version=1.0.0.0comparereports a parameter that gains or losesparamson a collection type as a changed declaration, as~ public static ImmutableArray<T> Create<T>(ReadOnlySpan<T> items) → public static ImmutableArray<T> Create<T>(params ReadOnlySpan<T> items) [body], where it listed the member as changed in its body alone or answeredNo member changes, and its comparison of such a member starts with the line that names both declarations. Between versions of the .NET SDK and of NuGet libraries, 92 members gainparamson such a parametercomparereports a method whose conditions changed as a changed declaration, as~ public static T CompareExchange<T>(ref T location1, T value, T comparand) where T : class → public static T CompareExchange<T>(ref T location1, T value, T comparand) [body], where it listed the method as changed in its body alone or not at all, and its comparison of such a method starts with the line that names both declarations. Where a condition names a class that gave way to one of the same name in another namespace, both declarations are written in full. Between .NET 8 and .NET 10, 75 methods in 6 libraries of the runtime change their conditionscomparelists a type whose own type parameters changed their conditions with the kindmetadata, as~ System.Func<,> [metadata]between .NET 8 and .NET 10, where it did not list the type; a type parameter renamed alone or awhereclause reordered does not count. Between those versions, 61 types change their conditions, 49 of them in the runtime's core librarycompareno longer lists the field that holds an event's subscribers beside the event, as- private EventHandler Closedbeside- public event EventHandler Closed, nor the other fields a compiler adds, unlessinclude_compiler_generatedis set. A public field that becomes an event reads- public EventHandler Changedbeside+ public event EventHandler Changed, where only the event was listed. Between .NET 8 and .NET 10, 4 member lists of the core library and of the Visual Basic library for Windows Forms lose 7 such linescomparelists a type whose type parameter gains, loses or swaps itsinoroutwith the kindmetadata, as~ Shop.ISource<> [metadata], where it did not list a type whoseinoroutalone changed. A class whose name does not count its type parameters, as F# names the classes it generates, and that gains or loses one under that name is listed with the kindmetadataas wellcompare, asked withtype_nameabout a type the list of changed types marksmetadata, ends its answer with the type's two declarations, as inDeclaration: public class Shop.Mover : IOwner<int> → public class Shop.Mover : IOwner<string>, where it answeredNo member changes in Shop.Moveralone or listed the changed members alone. The declarations read aslist_memberswrites a type's heading, with its base types, the conditions on its type parameters and theirinorout, and are written with every type's namespace where they would read alike, or where a class gave way to one of the same name in another namespace. Between .NET 8 and .NET 10, 125 types in 24 libraries end their answer so, 63 of which answeredNo member changesalone.compare's description says thattype_nameanswers what changed in that typecomparelists a struct that becomesreadonlyor aref struct, or stops being one, with the kindmetadata, as~ Shop.Cursor [metadata], where it listed it only for a member that changed as well or not at all, and its type view ends with both declarations, asDeclaration: public struct Shop.Cursor → public readonly struct Shop.Cursor. Between .NET 8 and .NET 10, 8 structs in 4 libraries of the runtime gain or losereadonlyorref, among them the publicSystem.Text.Json.JsonReaderState, which becomesreadonly; 3 of them were not listed at allcomparelists a class or struct that becomes arecordor stops being one with the kindmetadatabesidemembers, as~ Shop.Order [metadata,members], and its type view ends withDeclaration: public class Shop.Order → public record Shop.Order : IEquatable<Order>, where it listed only the members a record bringscomparelists an enum whose underlying type changes with its members kept with the kindmetadataalone, as~ Shop.Width [metadata], and its type view answersNo member changes in Shop.Widthwith the lineDeclaration: public enum Shop.Width : byte → public enum Shop.Width : short, where it showed the change only as the enum'svalue__field removed and added,- public byte value__and+ public short value__comparereads the lines of one member's name in a type's list of changes together and writes each with every type's namespace that names a class another of them names by the same short name, as- public void Swap(Shop.Alpha.Badge badge, int count)beside+ public void Swap(Shop.Beta.Badge badge, long count), where both readBadge. A line that names two different classes of one short name on its own is written in full as well. Between .NET SDK 10.0.303 and 10.0.402, MSBuild'sNtQueryInformationProcesstakes aPROCESS_BASIC_INFORMATIONfrom another namespace while it loses two parameters, which both lines showed as the same typecomparelists a method, constructor or indexer whose parameter's default changed, was gained or was lost with the kinddeclarations, as~ public void Open(int width = 5) → public void Open(int width = 6), and its member view begins with that line, where it answered that the two bodies are identical. Between .NET 8 and .NET 10, 9 types in 6 libraries of the runtime gain such lines, as~ public static void Assert(bool condition, string message) → public static void Assert(bool condition, string message = null)comparecounts two defaults as the same where they store the same value or read alike: a default of an enum type that names the same member after the enum's members were numbered anew is no change, while the new numbers show at the enum's own members, and a default one build writes as(Glaze)2because it did not find the enum's library is the same asGlaze.Frostedin the othercompare, asked withtype_namefor a type its list of changed types names as one build's, answers that only that build has it also where the other build has a class of that name with other type parameters, where it compared the two classes as one: a class that gains or loses type parameters, asSystem.Buffers.AsciiByteSearchValuesbetween .NET 8 and .NET 10, a static class beside a generic class of its name, asSystem.Collections.ObjectModel.ReadOnlyCollection, and a class nested in a class whose name the other build gives to a generic class, asSystem.Security.Cryptography.PemEnumerator.Enumerator, which answers5 member changes in System.Security.Cryptography.PemEnumerator.Enumerator, which only A has:where it answered8 member changes in System.Security.Cryptography.PemEnumerator.Enumerator:with the members of .NET 10'sPemEnumerator<TChar>.Enumerator. A type whose name stands for two generic classes in the other build is answered so as well, where the answer was an error that the name matches 2 types. Withmember_name,compareanswers that only that build has the method, where it compared it with the other class's method. Of the 7,609 types the lists of 766 pairs of libraries name as one build's, .NET 8 against .NET 10 and three .NET SDKs among them, 14 answered wronglycompare, asked for a type name without brackets that fits no class of either build as written and finds a different class in each, answers with an error that names both, asType name 'System.Buffers.AsciiCharSearchValues' matches System.Buffers.AsciiCharSearchValues<> in A and System.Buffers.AsciiCharSearchValues<,> in B. Name one as listed., where it compared the two classes as onecompareasked about one method that has no body in either build says so and why for each build, as inShop.Lid.Open() → void — neither assembly has a body for it: abstract in both., orextern, or provided by the runtime for a delegate's methods, where it said that the bodies are identical in both assembliescomparereads no method body of a reference assembly, such as a .NET reference pack, a .NET Framework reference assembly or a publicized copy whose bodies a tool removed, whose methods have no body, an empty one or one that throwsnull. Asked about one method, it saysA and B are reference assemblies, which hold no code to compare., or that one build holds no code to compare with the other's and then shows the other build's code, where it compared the stubs, said that the bodies are identical or showed a stub beside the other build's code. A member only such a build has is shown by its declaration in place of its stub, and the error for a field names no constructor of such a build as setting it or notcompare's list of changed types and its list of a type's member changes mark no body as changed where a build is a reference assembly, and say beneath the listNot compared: bodies; A is a reference assembly, which holds no code. A publicized copy of a game's main assembly against the full build of its version listed 9,106 types as changed, 3,096 of them for their bodies alonelist_membersandfind_methodsnow list a compiler-generated type thattype_namenames, whateverinclude_compiler_generatedsays, where they answeredNo membersandNo methodslist_memberswithname_containsnow finds a constructor by the name of its type, under which it is listed, as well as by.ctor; a static constructor is found the same waylist_membersandfind_methodsno longer list a parameterless constructor for a struct or an enum that declares none; the assembly holds no such constructor, and asking for it ended indefinitions contains null elementorSpecified cast is not validlist_membersnow writes the methods, properties and events of an interface by one rule, as C# writes them: a member the implementing type supplies has no modifier, where a property and an event carriedabstractand a method did not- A member of an interface with a default implementation is now written with
virtual, a sealed one withsealed, and a static one withstatic,static abstractorstatic virtual; before, a method with a default implementation looked like one without, and astatic abstractorstatic virtualmember looked like a plainstaticone - A member with which an interface implements a member of its base interface is no longer written with
sealed override, and one that makes the base member abstract again is written withabstract comparewrites an added or removed member of an interface the same waylist_memberswithinclude_inheritednow writesref readonlyon a parameter of a member inherited from another assembly's class, where it wrotereflist_membersandcomparenow write a method or a read-only property that overrides with a more specific return type as the override it is,public override Letter Copy(), and a sealed one aspublic sealed override Seal Copy(); they wrotepublic virtual Letter Copy(), and the sealed one without any modifier, as a method that is not virtual. C# allows such an override since version 9 on .NET 5 and later, and the compiler writes one for the copy method of a record that inherits from a recordlist_memberslists every member of a type, also a generic method beside a method of its name and parameters and conversion operators that differ in their return type alone, where it listed one of each such group; withinclude_inheritedit leaves out a base class's member only where a class below hides it, so a base class'sFind<T>(string)is listed beside a derived class'sFind(string); over a game's main assembly, a core library and two builds of a patching library, 38 types gain 241 memberslist_membersandcomparewritethisbefore an extension method's first parameter, as C# and the decompiled code do, as inpublic static void Run(this Host host)andpublic static void Bump(this ref Counter counter), where they wrote such a method like any static method;find_methodsand the names an answer passes on keep their form without it. Over a game's 106 libraries, 2,963 extension methods in 399 classes are listed this waylist_memberswritesparamsbefore a parameter that takes any number of arguments through a collection type, which C# 13 allows, as inpublic static ImmutableArray<T> Create<T>(params ReadOnlySpan<T> items), where it wrote the parameter withoutparams. The .NET 10 shared framework holds 82 such parameters in 5 libraries, and the .NET SDK's top folder 25list_memberswrites the conditions on a generic method's type parameters after its parameters, as C# writes them, as inpublic static T Tag<T>(this T value) where T : class, where it wrote the method without them, and those on a generic type's own type parameters after its base types in the heading, as in2 members of public struct System.Nullable<T> where T : struct:. The conditions readclass,struct,unmanagedornotnull, then the types, thennew(), and lastallows ref struct, which C# 13 allows anddecompiledoes not show, as inpublic static int SizeOf<T>() where T : allows ref struct. An override and an explicit interface implementation take the conditions of the member they override or implement and write none, as the decompiled code does. Of the 2,586 types of the .NET 10 core library, 255 gain conditions in theirlist_membersanswerlist_membersno longer lists the fields a compiler adds beside the member they belong to, as it already left out an auto-property's backing field: the private field that holds the subscribers of an event declared the short way, asprivate EventHandler Changedbesidepublic event EventHandler Changedwithaccessibilityall, the fields Visual Basic adds for an event and for an auto-implemented property, asprivate EventHandler ChangedEventandprivate int _Size, the fields F# adds for records, unions and closures, asinternal string Escaped@, and those an older C# compiler added for the lambdas it caches.include_compiler_generatedlists them, and theNot shownline counts them as compiler-generated. Withaccessibilityall, 8 of the 2,586 types of the .NET 10 core library lose such fields from their list, 5 of the 9,644 types of a game's main library and 113 of the 190 types of the F# build tasks librarylist_memberswritesinoroutbefore each variant type parameter of an interface or a delegate in its heading, as C# declares them:1 member of public interface System.Collections.Generic.IEnumerable<out T> : IEnumerable where T : allows ref struct:and4 members of public delegate System.Func<in T, out TResult> where T : allows ref struct where TResult : allows ref struct:, where it wrote each type parameter by its name alone. A type nested in a variant interface writes theinoroutof its own type parameters, as C# declares them there. 66 types of the .NET 10 shared framework gaininoroutin their heading, 55 of them in its core librarylist_memberswrites a parameter's default in a declaration as C# writes it, aspublic void Open(int width = 5)andpublic static Task<byte[]> ReadAllBytesAsync(string path, CancellationToken cancellationToken = default), an enum's value by its member's name, aspublic void Go(Pace pace = Pace.Fast), a combination of flags asEdges.Left | Edges.Top, and the number with the enum's name, as(Pace)7, where no member has it or the enum's library was not found. In the .NET 10 core library, the C# compiler library and a game's main library, 4,053 declaration lines of 11,174 types gain defaultslist_memberswrites a constant's value as C# writes it: aboolastrueorfalse, where it wroteTrueandFalse; a number with the suffix C# requires, as1.5m,0.5fand5000000000L, in the same form whatever the number format of the machine ILens runs on; a special value by its name, asint.MaxValue, but a constant of the type that declares it by its value, aspublic const int MaxValue = 2147483647; and a constant of an enum type by its member's name, aspublic const Pace Usual = Pace.Slow, where it wrote the numberlist_membersleaves out the fieldvalue__an enum keeps its value in, which no source declares and the decompiled code never shows, and counts the enum's members alone, as3 members of public enum Shop.Pace:and3 fields:for an enum of three values, where it listedpublic int value__and counted it as a fourth member. An enum without members answersNo members of public enum Shop.BlankwithNot shown: 1 compiler-generated; include_compiler_generated lists it, andinclude_compiler_generatedlists the field again. Each of the 19,667 enums of .NET 10, the .NET SDK, a game's libraries and its mods had that linecompareleaves the field out as well; the type view of an enum only one build has lists its members alonelist_memberswrites a name a declaration declares with the@C# needs before a reserved keyword, as the decompiled code does: a parameter, as inpublic Func(object @object, nint method)andpublic virtual IAsyncResult BeginInvoke(T arg, AsyncCallback callback, object @object), which every delegate's constructor andBeginInvokewrite, where it wroteobject object, and a field, a method, a property, an event and a type parameter, as inpublic int @base. A word that is a keyword only in a query or an async method, such asfromorawait, takes none, as C# accepts it in a declaration. In .NET 10 and a game's libraries, 1,743 names in the declarations of 901 types gain the@comparewrites its member lines and declaration lines the same way, as in~ public void Send(object obj) → public void Send(object @object), and lists the same members as changedlist_memberswritesabstract overridefor an abstract method, property or event that overrides a member of a base class, as C# and the decompiled code do, as inpublic abstract override string ToString()andprotected abstract override bool IsArrayImpl(), where it wrotepublic abstract string ToString()as for a new member that hides the base class's. That holds for one that returns a more specific type and for a generic one as well; a member declared abstract anew keepsabstractalone. In .NET 10, an SDK's top folder and a game's libraries, 283 lines of 85 types gainoverridecomparewrites its lines the same way, and lists a member that is declared abstract anew in one build and an abstract override in the other as a changed declaration, as in~ public abstract void Start() → public abstract override void Start(), where it found no change. A changed member that is an abstract override reads as one, as in~ public override bool CanRead { get; } → public abstract override bool CanRead { get; } [get_CanRead]decompilenow answers for a field of a struct that declares no parameterless constructor, where it ended indefinitions contains null elementfind_types,find_methodsandlist_membersnow say when their accessibility filter left matches out: an answer with results ends with a line such asNot shown: 15 internal or private, and an empty answer also names the setting that lists them, as inNo methodsfollowed byNot shown: 2 internal or private; accessibility all lists them- An empty answer from these tools also counts the matches left out as compiler-generated and names every setting needed to list them, as in
Not shown: 39 compiler-generated; include_compiler_generated with accessibility all lists them - A member name that a class and its base class use for different kinds of member, such as a field where the base class has a method, now means the member of the more derived class, as in C#, where
decompileandanalyzeanswered that the name is ambiguous and the derived class's member could not be reached at all; a method the name hides stays reachable withparameter_countorparameter_types, or through the base class - A field beside an extension method of its name is now answered with the field, where the name was ambiguous
- A constructor can now be asked for by the name of its type, as
list_membersprints it, indecompile,analyzeandcompare, where the answer wasMember 'Widget' not found;.ctorworks as before, and a static constructor is still.cctor .ctoron a struct with one declared constructor now answers withoutparameter_countorparameter_types, where the answer named two overloads.ctoror the type's name on a struct or enum that declares no constructor is now answered with a not-found error that says so and, for a struct, points toinstantiated_by;compareansweredbodies are identical in both assembliesfor it- A constructor is now looked for on the asked type alone:
.ctorwith a hint that no constructor of the class satisfies is answered with the class's own constructors, where the answer was a base class's constructor marked as inherited list_members,find_methods,decompileandanalyzenow writeinandref readonlyon a parameter of a virtual, abstract or interface method, of a delegate and of an interface's indexer; they wrote such a parameter as if it were passed by value,Peek(Big)forPeek(in Big), while the same parameter of a method that is not virtual had its keyword- Two overloads that differ only in such a keyword are now told apart in the lists and in the error that asks to choose between them, and
compareanswers for a class that declares two of them, where it ended inAn item with the same key has already been added - A parameter of a C++/CLI library that is a reference inside a modifier is now written with
ref compare,decompileandanalyzeasked by name for a field, a property or an event answer in a few milliseconds, since they no longer go through every class of the assembly for extension methods of that name at each question: on a game's main assemblycompareanswers in 3 to 4 ms where Release 10 took about 70 ms, anddecompilein about 4 ms where it took about 45 ms- Every tool writes a generic method's type parameters after its name, as in
Shop.Till.Get<T>(string), and a method made for type arguments with those arguments, as inShop.Till.Get<int>(string)in whatanalyzelists foruses; a method and a generic method of one name and parameters printed alike, so a list, an overload error or ausesanswer named two methods with one line, andusesmerged a generic method's calls with other type arguments into one member_namereads type parameters in brackets after a method's name,Get<T>,Get<int>orGet<>, indecompile,analyzeandcompare, so such a method can be asked for apart from its namesake; a name without them means the method without type parameters where the name has both, and with parameter types that only a generic method of the name has, that generic method- A type argument is written as a parameter type is, by its keyword or short name, as in
System.Collections.Generic.List<int>whereSystem.Collections.Generic.List<System.Int32>stood beside parameter types likeint, infind_harmony_dependencies, inanalyze'susesandimplementsanswers and in theirImplementationandOverridelines; over every tenth type of a game's main assembly about a third of theusesanswers hold such a line decompile,analyzeandcomparetakereturn_type, a pattern for the return type asfind_methodshas it, so that one of several conversion operators that share their name and parameter type can be asked for on its own, asop_Explicitwithreturn_typeinton a number type that declares one for each type it converts to; such an operator could not be reached, and the overload error listed its namesakes in lines that read alike. Over a game's 106 libraries and the .NET 10 runtime, 448 conversion operators share their name and parameter types with another- A parameter type written without a keyword means the overload that takes the value, where a method beside one that takes the value by reference, as an
outparameter or as a nullable value answered with an overload error:Truncatewithparameter_typesdecimalmeansTruncate(decimal)besideTruncate(ref decimal),intmeansintand notint?, and a name without type arguments means the type without them. A base class's method that fits the parameter types as written is found before a derived class's method they fit only loosely, so askingTask<TResult>forContinueWithwithAction<Task>answers withTask'sContinueWith(Action<Task>) - Type parameter names in brackets select a generic method where the count leaves several:
Take<U>meansTake<U>(T)besideTake<T>(T)in a class with a type parameterT - Every line that names a conversion operator writes its return type after it, as in
Shop.Coin.op_Explicit(Coin) → int, inanalyze's answers and headers,call_sites,search_codeand the overload errors; operators that differ in their return type alone stood as one line in ausesorused_byanswer.find_methodslists such operators by their return type - A line that would read alike another line of its answer is written in full, every type with its namespace and enclosing classes, as
public void Take(Shop.Alpha.Badge badge)besidepublic void Take(Shop.Beta.Badge badge), inlist_members,find_methods,analyze,call_sites,search_code, the overload errors andcompare's member list and member view, and so is a header that names the member asked about where a namesake reads alike;list_membersprinted such a pair as the same line twice parameter_typesandreturn_typeread a?after a type as C# does, infind_methods,decompile,analyzeandcompare, also inside a type argument or an array's element:int?means the nullable type alone,string?the typestringandT?a type parameter's nullable form or the type parameter itself; such a pattern matched nothing, so a method whose parameter types were copied from the decompiled code, asEnumerable.Sum(IEnumerable<int?>), answeredNo overload ... matchesorNo methods- Every answer writes a nullable value type as C# and the decompiled code do,
int?where it wroteNullable<int>andT?forNullable<T>, inlist_members,find_methods,analyze,compare, the overload errors andfind_harmony_dependencies; a nullable type before a member's name keeps its name, as inSystem.Nullable<int>.GetValueOrDefault(). Over a game's main assembly the member lists of 610 of 9,644 types change - Every answer writes an array of two or more dimensions as C# and the decompiled code do,
int[,]andint[,,]where it wroteint[], inlist_members,find_methods, the headers ofdecompile,analyzeandcompare,compare's member lists, the overload errors andfind_harmony_dependencies; overloads that differ in an array's dimensions alone, which printed as one line, print apart, anduseslists a call of each where it merged them into one line. An array of arrays is written with the outermost array's brackets first,int[][,]for an array of two-dimensional arrays, also in ausesline that names an array's own member, which wroteSystem.Int32[][,]for a two-dimensional array of arrays. Over a game's 106 libraries, 66 of 456,814 members hold such an array parameter_typesandreturn_typeread an array's dimensions infind_methods,decompile,analyzeandcompare:int[,]means a two-dimensional array, and an array of arrays is read outermost first; held exactly, as a hint is first,int[]means an array of one dimension, so a method beside one that takes a two-dimensional array can be asked for on its own, where both answered with an overload error saying theycannot be narrowed furtherandint[,]matched nothingdecompile,analyzeandcomparename an extension method found from the type it extends under the class that declares it, and its mark names the type it extends:// Shop.TillExtensions.Open(Till) → void [extension method on Shop.Till], wheredecompilewrote// Shop.Till.Open(Till) → void [extension method on Shop.TillExtensions], naming a methodShop.Tilldoes not have, andanalyze's mark named the class that declares the methoddecompile,analyzeandcomparefind an extension method that a Visual Basic module declares from the type it extends, as they find one of a C# static class: asked forWaveonShop.Gizmo,decompileanswers with// Shop.GizmoModule.Wave(Gizmo) → void [extension method on Shop.Gizmo]where it answered that the type has no such member, also not as an extension method. Every static method withExtensionAttributecounts, whatever its class, by the rulelist_memberswritesthisby; six Visual Basic libraries of the .NET SDK hold 1,632 such methods- They find an extension method whose receiver is passed by
ref,inorref readonlyfrom the type the receiver refers to, asBump(this ref Counter counter)fromCounter, and one whose receiver isIntPtrorUIntPtr, which the decompiled code writes asnintornuint, fromSystem.IntPtrorSystem.UIntPtr, where they answered that the type has no such member; a game's 106 libraries hold 267 extension methods whose receiver is passed by reference and is a type of their own library decompile,analyzeandcomparefind an extension method that the inspected assembly declares for another library's type, or for a base class or interface of it, when asked through that type, as through a type the assembly defines:decompileshows its code under// Shop.KettleExtensions.Boil(Kettle) → void [extension method on Lib.Kettle]where it answered thatLib.Kettleis not defined in this assembly,comparecompares its bodies where it answered that neither assembly defines the type, andanalyzeanswers every kind for it,call_sitesanduseswhere they were refused andused_bywhere it answeredNo users. A game's libraries, mod libraries and the .NET runtime hold 931, 2,835 and 1,061 extension methods for another library's type; asked through it with their parameter types, 930, 2,831 and 1,057 are now answered- A name the other library's type has as a public instance member keeps the answer about that member, as does an internal one where the library grants the inspected assembly its internals through
InternalsVisibleToand, through an interface, a member its base interface declares, and parameter types that fit only the extension method select it; a protected, private or static member of the name, or an internal one the library does not grant, leaves the extension method to answer, as C# binds a call on a value outside the type's derived classes, while a hint that fits a static or protected method, such asparameter_count0, answers about that method, also where the extension method takes the same parameters.call_sitesandcompare, which look for a method, find the extension method also where the type has a property or a field of the name, whiledecompileandused_byanswer about the property or the field - Where ILens cannot load the other library, an extension method for the type itself is found unless the assembly's references name a member of that name on the type, and the answer says what was not read:
Not checked: whether Lib.Kettle or its base types have a member Boil, which C# would take first; ILens could not load Lib, Version=1.0.0.0; the same line names a base interface of the type that cannot be read decompile,analyzeandcomparefind an extension method whose receiver is a type parameter through every type that fits the method's constraints, as C# calls it there: asked forCornersthrough a class that implements the interface the type parameter asks for,decompileshows its code under// Shop.ShapeExtensions.Corners<T>(T) → int [extension method on T where T : Shop.IShape]where it answered that the type has no such member, also not as an extension method. Through another library's type the same holds where ILens reads that library, and where it cannot, only such a method without any constraint is found. A game's libraries, mods, the .NET runtimes and the SDK hold 236, 84, 55 and 553 such methodsdecompileandanalyzeanswer with the property, field or event of a name where several extension methods of that name apply to the type as well, where they answered with an overload error naming the extension methods; the .NET runtime and the SDK hold 275 such pairs of a type and a name- An answer that names an extension method found through a type whose base class ILens cannot load ends with
Not checked: whether Lib.Widget or its base types have a member Polish, which C# would take first; ILens could not load Lib, Version=1.0.0.0, since a member of that class would be what C# calls decompile,analyze,list_members,find_methodsandcompareread a type name without type parameters as the type without them that the inspected assembly references, where the assembly defines only generic types of that name, as C# reads the name in the assembly's code: in an assembly that definesShop.Marker<T>andShop.Marker<T, U>and uses another library'sShop.Marker,analyzewithused_byonShop.Markerlists the users of that library'sShop.Marker, where every tool answered that the name matches the two generic types and left out the type it means. A game's 106 libraries hold 2 such names, the mods, the .NET runtimes and the SDK nonecompareanswers such a name withType 'Shop.Marker' is not defined in either assembly; it comes from Lib, Version=1.0.0.0., anddecompile,list_members,find_methodsandsearch_codewithShop.Marker is not defined in this assembly; it comes from Lib, Version=1.0.0.0. This assembly defines Shop.Marker<>, Shop.Marker<,>., which names the generic types a caller may have meant- An assembly that defines one generic type of such a name no longer has the name answered about that type:
analyzewithused_byonShop.GadgetansweredNo users of Shop.Gadget<>for an assembly whose code uses another library'sShop.Gadget, and now lists that code - A name that generic types of the assembly and generic types it references share is an error naming both groups,
Type name 'Shop.Crate' matches 2 types: this assembly defines Shop.Crate<,>; it references Shop.Crate<> from Lib, Version=1.0.0.0. Name one as listed., where the name was answered about the assembly's one type of that name or the error listed its own types alone;Type not foundfor a count of type parameters no type of the name has lists them in the same two groups. A game's libraries hold 2 such names and the .NET Windows Desktop runtime 1 decompile,analyzeandcomparetake an array type astype_name, written as C# writes it, such asbyte[],System.Byte[],Shop.Part[],int[,]orint[][], to reach the extension methods code can call on such an array, which no question reached before but through the class that declares them:analyzewithextension_methodslists the methods for exactly that array and for an array of a type parameter the array fits, as3 extension methods for System.Byte[]:, anddecompile,analyzeandcomparefind one by name through the array, as C# binds a call on it, also one for an interface the array implements,System.Arrayorobject, under a header marked[extension method on System.Byte[]]or[extension method on T[] where T : struct]. Before,decompileansweredType not found: byte[].andanalyzeNo extension methods for byte[]withThis assembly neither defines nor references a type of that name.A game's libraries, the .NET 10 runtimes and the .NET SDK hold 475 extension methods for an array- A public instance member of
System.ArrayorSystem.Objectof the name comes first and is answered as asked through that class, as C# binds it, while a static member ofSystem.Arraysuch asReverseorResizedoes not keep an extension method of its name out; forcall_sitesandcompare, which look for a method, a property ofSystem.Arraysuch asLengthdoes not keep it out either - Every other question answers an array type with
byte[] is an array type, which only analyze with kind 'extension_methods' and decompile, analyze and compare with member_name take, to find the extension methods code can call on it., wherelist_membersansweredType not found: byte[]. - Where ILens cannot load the library of the array's element, the answers say what was not read, as
Not checked: whether more extension methods apply to Lib.Beam[] through what Lib.Beam derives from; ILens could not load Lib, Version=1.0.0.0 decompile,list_membersandfind_methodsanswer a question about a type the inspected assembly forwards to another assembly with that assembly, asSystem.Net.ServicePoint is not defined in this assembly, which forwards it to System.Net.Requests, Version=10.0.0.0., where they answeredType not found: System.Net.ServicePoint.100 of the 172 libraries of the .NET 10 shared framework forward types, System.Runtime 936, mscorlib 1,386 and netstandard 2,611. A type the assembly both references and forwards is answered with the forward as well, where the answer saidit comes from ...- A name without type parameters that several generic types the assembly forwards share is an error that lists them, as
Type name 'System.Func' matches 17 types this assembly forwards: System.Func<>, System.Func<,>, ... Name one as listed., where every tool answeredType not found, and beside types of that name the assembly defines or references they stand in a group of their own, asthis assembly defines Shop.Dial<>; it forwards Shop.Dial<,> to Core, Version=3.0.0.0. A name without type parameters means the forwarded type without them, as it means a referenced one analyze, asked about a type the assembly forwards, ends its empty answer withThis assembly forwards it to System.Private.CoreLib, Version=10.0.0.0., and asked about a member of such a type withThis assembly forwards System.Object to System.Private.CoreLib, Version=10.0.0.0., where both saidThis assembly neither defines nor references a type of that name.compare, asked about a type neither build defines, names where each build that forwards it forwards it, asType 'System.Net.ServicePoint' is not defined in either assembly; A and B forward it to System.Net.Requests, Version=10.0.0.0., where it answeredType 'System.Net.ServicePoint' not found in either assembly.; beside a build that only references the type it reads...; in B it comes from Lib, Version=1.0.0.0; A forwards it to Core, Version=3.0.0.0.- Through a class of the inspected assembly whose base class from another library has a private member of a name, or an internal one the library does not grant,
decompile,analyzeandcompareanswer with the assembly's extension method of that name, as C# binds it, where they answeredApp.OwnShelf inherits Lib.Shelf.Dust() from a base class this assembly does not define; .... Where nothing else answers to the name, that error stays, and where a class further up has a member of the name the class can reach, the error names that member, asSystem.Delegate'sHasSingleTargetin place ofSystem.MulticastDelegate's, which a delegate type cannot reach decompile,list_membersandfind_methods, asked for a type by a name that finds no type, name the types of that name in another namespace, asType not found: Mall.Basket. Types named Basket: Shop.Basket., where they answeredType not found: Mall.Basket.alone. Where no type differs only in the namespace, every type of the name is named, one nested in another class included, so a name that leaves out the class a type is nested in finds it too. A type the assembly references or forwards is named with the library it comes from or goes to, and at most 40 types are named, the rest counted. Of 676 such answers for names taken from a game's main library and a mod, 669 name exactly one typeanalyze, asked about a type nothing in the assembly names, ends its empty answer with the same list afterThis assembly neither defines nor references a type of that name., so a name under the wrong namespace no longer reads like an unused typecompare, asked about a type neither build has, names the types of that name either build has, asType 'Mall.Basket' not found in either assembly. Types named Basket: Shop.Basket., and where the builds differ, which build has them, asIn B, types named Banner: Shop.Banner.. It also names the types that share the name with another count of type parameters, as the other tools do, where it named none- The mark of an extension method found through a type, whose receiver is a type parameter, writes
allows ref structamong its conditions, as[extension method on T where T : allows ref struct] decompile,analyzeandcomparetake a member name written with the@C# needs before a reserved keyword, asmember_name@paramsfor a field the decompiled code writespublic TypeArray @params;, where the answer wasMember '@params' not found, andanalyzetakesparameter_name@switchasswitch, where it answered that no overload has a parameter named@switch. An@before a word that is no keyword stays part of the name, since F# begins names with it- An answer to a call with
limit0 no longer has an empty line between its header and the line for what the limit cut, infind_types,find_methods,find_harmony_dependencies,compareand every kind ofanalyzeexceptcall_sites - A list asked with a negative
limitnow answers like one asked withlimit0, with its header and the count of what was left out, where the last line named more results than the list holds, as in21 types:followed by... (24 more; raise limit to see all)forlimit-3;list_membersanswered this way already - A value a parameter does not take is now answered with an error that names the parameter and what it takes, as in
Invalid value 7 for argument 'accessibility'. Valid values: public, public_protected, all.orInvalid value 1.5 for argument 'limit'. Expected an integer., where the answer was aJsonExceptionthat named an internal type such asILens.AccessibilityFilterand a byte position, and named no parameter - A call with several such values now gets one answer that names each of them, and a value is repeated up to its first 40 characters
- A number or several comma-separated names for a parameter with listed values are now refused with that error, where they were answered as one of the listed values:
list_memberswithkinds"field,method"listed the fields alone, withkinds[99]it answeredNo members, andfind_typeswithaccessibility99answered as withall - A list of parameter types that holds
nullis now refused, wherefind_methodsansweredNo methods - A listed value in another case is now taken with or without its underscores, as in
Public_Protected, where only the form without them,PublicProtected, was - A whole number with a zero fraction, such as
1.0, the text"true"or"false"for a boolean, andnullfor any optional parameter are now taken,nullas the parameter left out; each was an error,nullforlimit, for a boolean and for a parameter with listed values - A required parameter that is left out or given as
nullis now answered withMissing required argument 'kind'., where one left out was answered with a sentence that ended in(Parameter 'arguments') parameter_typesnow reads several types written as one text,"string, int", as the list of them, on its own or as an element of a list, wherefind_methodsansweredNo methodsanddecompile,analyzeandcompareanswered that no overload matches parameter types(string, int)while listing an overload with exactly those; a comma inside angle or square brackets, as inDictionary<int, string>, stays in its type- The error for
get_Item,set_ItemorItemon a type whose indexer has another name now ends with that name and its accessors, as inits indexer is named Chars: System.String.get_Chars(int), where it said only that no such member exists - The overload error says to write the type parameters after the name where the candidates differ in them alone, where it pointed to
parameter_types, which cannot tell them apart, and a name whose count of type parameters fits no method is answered with the methods of that name - The overload error says what tells the candidates apart:
return_typewhere they differ in their return type alone, the type parameters after the name, or the parameter types written as listed where those given fit more than one - An allowed folder given through a junction or a symbolic link now admits the assemblies in it, addressed through the link or by their real path, where every request ended in
Path resolves (via a symbolic link or junction) to a location outside any allowed root - References in such a folder are now found when decompiling, where they stayed unresolved
- The link of such a folder is resolved once, when the server starts; when the link is moved to another folder while the server runs, requests through it are refused until a restart, and the folder it pointed to stays allowed
- The two refusals that list the allowed roots now add a root's resolved path where it differs from the one given, as in
Allowed roots: C:\Link (resolved: D:\Target) - The server now ends at startup with a line naming the folder when it cannot resolve an allowed folder, such as one below a folder it may not list, where it started and answered every request for that folder with an error
- An answer that leaves something out for want of a class of another assembly now says what was found for the class's library.
list_memberswithinclude_inheritedand the error for a member that was not found ended such an answer withILens could not load Lib, Version=1.0.0.0also when a library of that name was loaded without the class. They, and theNot checked:lines ofanalyzewithapplied_to,implements,uses,used_byandassigned_by, now name the file and its version:Lib as loaded from C:\libs\Lib.dll, Version=2.0.0.0, does not hold Lib.Widget; referenced is Version=1.0.0.0. An assembly built against an older version of a library gets this line, and one whose framework classes are read from another .NET than the one it runs on - A file of the library's name that holds another assembly is named with what it holds:
ILens could not load Lib, Version=1.0.0.0: the file found, C:\libs\Lib.dll, holds the assembly Other - A class the library forwards to another library is followed there, and the answer names the library the class is missing in or that could not be loaded
- An assembly that names one class through two libraries, of which one holds it, gets the library the class is missing in
- ILens now runs on .NET 10.0.14 instead of 10.0.12, with the .NET security fixes of October 2026
- The user guide describes
search_code, the startup option--max-search-seconds, and every tool's parameters, answers and errors as this release gives them - The README names
search_codeamong the tools whose output is C# - The token-cost table in the README and the user guide is measured on 2026-10-31