-
Notifications
You must be signed in to change notification settings - Fork 277
Make sealed/internal all classes that could be sealed/internal #869
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
Conversation
@Romfos What is the motivation for this change? What is it solving? It probably doesn't have any practical implication, but then if it doesn't - why to do it? I remember an argument about sealed types being a bit faster, as calls could be de-virtualized. But it only happens for virtual calls and we don't have those. Of course we have an argument of code design, but then we should just make types internal. That would make way more sense. I feel that typical .NET code is not having I feel that this change will just force us to make declaration of types more verbose for no practical reasons at all. And if somebody is extending NSubstitute (I've done that many times in my projects, because I know its source code 😉), then we are creating a potential inconvenience (even though I personally always favor composition over inheritance). I do appreciate you putting your effort in making the code being nicer. You spend time and I don't these days, so it's totally fare that you have a higher "privilege" to decide 😉 I just give my 5 cents here because I feel that this change makes code more verbose and inconvenient for no good reasons. Sealing everything is an industry standard as much as I am aware of. And having to seal every single class you create in the future - it's just annoying to me. |
Hello, Goal of this change: Segregate "public api" and other internal stuff 1. Extensibility based on interfacesNow all classes are public and are not sealed For example, some time ago you created a list of possible improvements #551 There are some improvments like:
All these changes technically are breaking change in current arch. In case of internal classes we can control and make changes like that absolutely safe, because public classes are not changed Proposed extensibility strategy:
2. Transparent version policy:major change:
minor change:
patch version:
This change allow on code review stage estimate impact for proposed change Mostly this is not relevant to NSubstitute, but in general good to know:
At the same time for NEW types (that are added in .NET 6+). They are also using sealed. At the same time they have segregation like: code that should be performant (like span api) and places when they don't care. It depends Anyway we can continue current approach and still do all types as public, it just make support more tricky |
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.
Appreciate @zvirja's concerns about limiting extensibility, but happy to leave the final call to @Romfos. Perhaps as a compromise we can focus on classes that almost definitely have no resuse value (internals like CastleForwardingInterceptor
, rather than handlers/formatters?)
Perhaps should also specify action on BreakingChanges.md if people get stuck with this (probably they need to raise issue with us to restore public visibility).
BreakingChanges.md
Outdated
* Make internal all classes that could be internal | ||
* Make internal all classes that could be internal |
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.
chore: duplicate entry
namespace NSubstitute.Core.Arguments; | ||
|
||
public class ArgumentFormatter : IArgumentFormatter | ||
internal sealed class ArgumentFormatter : IArgumentFormatter |
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.
thought: there might be an argument for keeping this one public? If someone is implementing their own arg matchers they may want to reuse formatting. WDYT?
/// This is to help prevent static state bleeding over into future calls. | ||
/// </remarks> | ||
public class ClearLastCallRouterHandler(IThreadLocalContext threadContext) : ICallHandler | ||
internal sealed class ClearLastCallRouterHandler(IThreadLocalContext threadContext) : ICallHandler |
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.
question: not sure if it is worth keeping handlers public in case people want to extend with their own routes?
sorry for delay. Looks like this pr require more time to work |
Hi @zvirja , any additional thoughts on this? Given your experience with extending nsub, are there any particular classes we really should keep public? |
IMO there is too much public visible and that makes things hard to change. Or we cannot change it, or we have to bump many times the major version. For example, https://github.com/nsubstitute/NSubstitute/blob/main/src/NSubstitute/Core/CallFactory.cs, is that really needs to be public? Of course we cannot make everything internal, it's a balance trick. It's a bit hard, as some part aren't documented for public usage and then you have to guess. IMO it is not a bad approach, when doubting, to make it internal and wait for feedback. It's also in benefit for the end-user that we could do breaking changes in internal code :) |
@dtchepak could you please merge this PR? |
@dtchepak Could you please help to align common vision for NSubstitute future:
|
It's approved so we could merge it, right? |
What about another compromise, when we keep this public, but move them let's say to My concern is extensibility. Often - adhoc extensibility, when I could hack some things quickly and use that in the project. You never known what you need. Even though I would admit that I do that rarely. As for the I think we are solving the problem we never had in practical terms. I cannot remember people coming and complaining that we broke something and they actively used it. We don't break things often either and we are not too shy to just raise a major version for that. I do again want to stress that I do appreciate the effort you do. It's not a game changer to me, so follow either way and I'll adjust. My personal opinion - to do that. But I am not an active maintainer - so take it with a multiplier 😊 |
I think probably soms large refactors are skipped because of the public interface. And also as user, it's confusing there is so much public.
User could always code the source code. And also, with extensibility it more useful to know the goal instead of the solution. So if we get any requests, we should ask for the goal (and not the solution)
I could live with it, but I think it's not the best choice. Because of the goal/solution topic |
I think both @Romfos and @zvirja have very valid points. As @zvirja points out, as he and I aren't able to be as active with contributions at the moment I think it is ok for @Romfos to have the final decision (taking into account the valid points raised on both sides of the discussion). For me, I think this compromise sounds good:
I've approved this PR and it's fine for it to be merged without changes. @Romfos please merge when you are happy with it. I've invited @304NotModified to have merge access as well. Even if we all have different opinions on how to get there, I know we all have our users' best interests in mind and if later on we find out another option seems better then we can always change it and bump up the version number. ❤️ |
Why is this pr closed? (And a force push afterwards? ) |
looks like git automatically closes PR if i do sync with master branch in my fork @304NotModified According to @dtchepak comment we need different implementation. Sorry little bit busy at this moment of time. I will comeback with new changes later or someone else could implement it |
@dtchepak I don't have merge access nor I can't reopen this pr. |
actually we don't need this changes, we need make new |
I think I've fixed this now 🤞 |
Changes:
note 1: technically this is breaking change, but de-facto no any changes required for 99% of customers
note 2: some "Core" types are still public (but could be sealed) because they are used in public interfaces and make them internal could be a "more real and impactful" breaking change. But we can make a part 2 for this in future, see #868
Related #830