-
Notifications
You must be signed in to change notification settings - Fork 982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: implement generic collections without signature changes #2999
Conversation
@@ -4367,13 +4370,17 @@ public void CopyTo(object[] destination, int arrayIndex) | |||
|
|||
void ICollection.CopyTo(Array destination, int index) | |||
{ | |||
InnerList.CopyTo(destination, index); | |||
((ICollection)InnerList).CopyTo(destination, index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will be breaking in terms of parameter validation and exceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I didn't write that PR with keeping exception compatibility in mind. I changed the types of private collection members in anticipation of it making nullability easier to implement, and it being slightly more performant (less typecasts in some enumerators, not sure if that applies here). It would be possible to not change any private collection members at expense of additional (small) runtime overhead.
That said, if you compare the ICollection.CopyTo
implementation of List<T>
between Desktop Framework and .NET Core you can see they already are doing "far worse" breaking changes than what you get by switching from ArrayList
to List<T>
on Desktop Framework. The .NET Core team should have had plenty of time to address compatibility concerns by now, if they think it is ok to break exception compatibility of basic collection classes then WinForms shouldn't try to keep it (they could arbitrarily break you anytime.)
PS: thats not intended as a generalized statement, just my opinion on this very specific case.
[edit] I see a line in the PR description "this PR attempts to make no breaking changes except [...]" might have been misleading; it was meant to be no breaking changes regarding binary compatibility (i.e. public signature changes). Updated the PR description accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will be breaking in terms of parameter validation and exceptions
Could you please be a little more specific?
I created a simple benchmark to assess any possible performance gains for this change. [MemoryDiagnoser]
public class FormControlsBenchmarks
{
private Form _form;
[Params(10, 100, 1000)]
public int N;
[GlobalSetup]
public void Setup()
{
_form = new Form();
foreach (int i in Enumerable.Range(1, N))
{
var label = new Label { Name = $"label{i:####0}" };
var textBox = new TextBox { Name = $"textBox{i:####0}" };
var button = new Button { Name = $"button{i:####0}" };
_form.Controls.Add(label);
_form.Controls.Add(textBox);
_form.Controls.Add(button);
}
}
[GlobalCleanup]
public void Dispose()
{
_form.Dispose();
}
[Benchmark(Baseline = true)]
public string EnumerateControls_GetEnumerator_var()
{
string name = null;
foreach (var c in _form.Controls)
{
name = ((Control)c).Name;
}
return name;
}
[Benchmark]
public string EnumerateControls_GetEnumerator_explicit()
{
string name = null;
foreach (Control c in _form.Controls)
{
name = c.Name;
}
return name;
}
}
The results are nowhere near as impressive as collected in #2644 (comment). In fact going from 1 to 2 saw a slight degradation. [EDIT] The results are a somewhat unstable and fluctuate, but overall they hover around these figures. |
I have also cherry-picked the
The results are a somewhat unstable and fluctuate, but overall they hover around these figures. |
The major holdback for performance is probably that we cannot change the underlying collection type (the member where the values are stored) for most collection classes because it is exposed as part of the public API and/or inheritance hierarchy prevents specializing into a particular content type. This means we don't get any of the performance advantages because we still call the non-generic API internally. At this point I don't think performance should be a decision factor, since its clearly not achievable to upgrade. The main point of doing the PR at all is LINQ support, and the yet to be decided upon point (which PR to take) depends on the roadmap for nullability support WinForms wants to provide when its finally turned on by default in VS/Roslyn. |
Closing as the current approach was rejected in favor of compatibility. More detailed commenting on the issue itself. |
Contributes to #2644
Alternative implementation to PR #2749
This is a WIP PR for exploring the impact #2644 may have and whether its worth taking the breaking change. This PR attempts to make no breaking change to public signatures except implementing additional interfaces.
This will allow LINQ support and other extension methods pick up the member type of the list, but the major downside of not allowing breaking changes is that plain
foreach
loops will not pick up nullability annotations because they need the return type ofGetEnumerator
adjusted, which is a breaking change (see PR #2749 for an implementation attempt)Notes:
IList
methods (where available), this makes for weird placement since not allIList<T>
methods are together, but considering I don't really want to reorder the existing methods (unless asked to) I figured it'd be the best solution to minimize diffs for now.Remove
implementations don't tell the caller whether they removed something, butIList<T>
requires this in its API. In some places where collections can be subclassed it is not easily possible to obtain this information, which leads to a bit weird looking implementation. Depending on how much you want to assume about subclass behavior these could be simplified.Remove
implementations whether the item to be removed is actually part of the collection. They run logic which severs the item and partially disconnects it from its true owner, but without notifying that owner. I did not fix these bugs yet (some are marked with comments though), I'm going to create a separate issue for this problem (so it doesn't have to wait for this issue/PR, which probably will take a while to dicscuss and resolve all points).Assert.Contains
orAssert.DoesNotContain
. Minor inconvenience which is easily fixed.Microsoft Reviewers: Open in CodeFlow