Expose TDLib object vectors as List<T> - #3366
Draft
FrayxRulez wants to merge 3 commits into
Draft
Conversation
Objects are read - bound to ItemsSource, iterated per render - so List makes the concrete type visible to the analyzer, stops foreach boxing an enumerator and lets the indexer inline. Functions are only ever written and serialised, and keep IList. Constructors take IList either way, so arrays and collection expressions still bind, and a parsed response is cast rather than copied. AnimatedImage draws no shimmer when Outline is null, hence the shared empty. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A binding does not assign the property: it calls a Set_ on XamlBindingSetters, which takes the value as object and belongs to this assembly, so nothing flagged it. Following that one call reports 83 collections bound from XAML with no CCW vtable - each one a page that throws when opened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The parameter and the field are the same type now, so TdCollection.AsList is gone. Call sites became [x] and [], which allocates less than it did: the converter copied every array into a list. Four needed more than a rewrite. TryGetColors and the two chat-list readers fill by Add rather than by index, because a List's capacity is not its count. TextStyleRun shares one empty list, since Array.Empty was free and these sit on the render path - a shared mutable list is a footgun, and notes has the plan for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
FrayxRulez
marked this pull request as draft
August 17, 2026 11:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Generated TDLib objects expose their vectors as
List<T>instead ofIList<T>. Functions areleft alone.
Why
A XAML binding assigns through the declared type, so with
IList<T>the concrete type only existsat runtime and nothing can see it at compile time. That is how
SettingsStoragePagecame to bindStatistics.ByChattoItemsSourceand fail withE_INVALIDARG, with no warning anywhere. WithList<T>the type is visible in the generated binding code, and the analyzer reports it.Two more reasons, both on paths that run per render:
foreachover anIList<T>boxes the enumerator.List<T>has a struct one..Entitiesalone isiterated in 23 places.
Countinline instead of dispatching through the interface.Functions gain none of this - they are only ever written and serialised, never bound or iterated -
and keeping
IList<T>there leaves 93 call sites able to pass an array. So they keep it.Shape
List<T>; nested vectors areList<List<T>>.List<T>, so nothing changes at runtime for aparsed response. The nested readers now build
List<List<T>>and theWriteArrayoverload forthem was widened to match.
new[] { x }andArray.Empty<T>()to[x]and[], which allocatesless than the intermediate step did - the converter copied every array into a list.
Worth a look in review
TryGetColorsand the two chat-list readers fill byAddrather than by index. AList'scapacity is not its count, so the literal translation of
new T[n]would throw.TextStyleRun.NoEntitiesandAnimatedImageSource.NoOutlineare shared empty lists, becauseArray.Empty<T>()was free and these sit on the render path. A shared mutable list is afootgun:
IList<T>.Addon an array used to throw, and now it silently corrupts a singleton.Inheriting a frozen
List<T>cannot help - the members are not virtual, so the guard is inertwhenever the static type is
List<T>, which it always is here. The plan is to return a fresh listfrom
GetEntities, whose result callers may edit, and to add a debug-only check that the count isstill zero.
MessageServiceText.ReplaceWithLinkloses anif (Entities.IsReadOnly)copy-guard that onlyexisted because an
IList<T>could be a read-only array wrapper.foreach (… in x.Reverse())became index loops:List<T>.Reverse()is in-place and returnsvoid, so they no longer compile as written. The one bare
x.Reverse();statement in the codebaseis on a different type and is unaffected.
Builds clean. Not run.
Note on the diff
This stacks on three commits that are on
developlocally but not yet pushed, so the PR currentlyshows six. The first three drop out once
developis pushed.