-
Notifications
You must be signed in to change notification settings - Fork 458
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
Arguments class and Resolve methods refactor #444
Conversation
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.
Generally looks good to me, although I didn't review extensively. Here's a little feedback after reading the long discussion in #420. Feel free to make use of, or disregard, these comments as you see fit; I don't wish to hold this PR back.
src/Castle.Windsor/Windsor/Extensions/WindsorContainerExtensions.cs
Outdated
Show resolved
Hide resolved
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.
I agree with @stakx's comments and I have some more minor comments.
Arguments.FromFoo(...)
static factory methods don't seem to be here. I personally like those as an API better than new Arguments().InsertFoo(...)
as an API, reading through the rest of the PR. Same as how I prefer ImmutableArray.CreateRange(...)
to ImmutableArray<T>.Empty.AddRange(...)
.
I don't feel strongly since it looks like I'll just be using the Resolve(IReadOnylDictionary) extension methods anyway.
src/Castle.Windsor/Windsor/Extensions/WindsorContainerExtensions.cs
Outdated
Show resolved
Hide resolved
There is a few different bits here I'm responding to. I know we keep making changes but I think we are nearly home with the comments. You can't do Here is the current state in the pull request, with some comments: Arguments()
Arguments(string name, object value)
Arguments(Type type, object value)
Arguments(IDictionary values)
// it would be good to have an IEnumerable<KeyValuePair<string, object>> constructor,
// but it'll be ambiguous with IDictionary which is more important here so you can pass
// in both named and typed parameters
void Add(object key, object value) // throws
Arguments Add(IDictionary arguments) // doesn't throw
Arguments AddNamed(string key, object value) // none of the rest throw
Arguments AddNamed(IEnumerable<KeyValuePair<string, object>> arguments)
Arguments AddNamedProperties(object instance)
Arguments AddTyped(Type key, object value)
Arguments AddTyped<TDependencyType>(TDependencyType value)
Arguments AddTyped(params object[] arguments) // this handles one or more objects
// should we have an IEnumerable overload here? Will that just be ambiguous with the object one? I think we should do something about one Factory methods vs constructor arguments, I was content with the factory methods which I proposed originally but at some point along the line in the other pull request they were changed. Annoyingly the problem we've got now is that we don't have and can't have a constructor that accepts an new Arguments("A", 1)
.AddNamed("B", 2);
Arguments.FromNamed("A", 1)
.AddNamed("B", 2);
Arguments // same as 2nd example just formatted differently
.FromNamed("A", 1)
.AddNamed("B", 2);
Arguments
.WithNamed("A", 1)
.WithNamed("B", 2); I don't think we can use If we were to change back to factory methods rather than constructors we'd remove 2 constructors and then get the benefit of being able to use all the named and typed overloads for the first set, like this: Arguments()
Arguments(IDictionary values)
// obviously the current 3+3 AddNamed and AddTyped instance methods
static Arguments FromNamed(string key, object value)
static Arguments FromNamed(IEnumerable<KeyValuePair<string, object>> arguments)
static Arguments FromNamedProperties(object instance)
static Arguments FromTyped(Type key, object value)
static Arguments FromTyped<TDependencyType>(TDependencyType value)
static Arguments FromTyped(params object[] arguments) |
Right. Just to clarify, it’s
This is a common thing to do that does not result in ambiguities, even if you pass a null literal.
Shouldn’t they both throw? Why would it make sense for the user to pass things that
I’d add a private constructor to enable
I’ve seen
Seems good to me.
Thought this one was going to be |
Disclaimer: I don't know the Windsor code base well, so I am looking at Throwing vs. non-throwing versions of
|
@jnm2 Done. Looking at it more now I don't think an
@jnm2 I was referring to the fact that
@jnm2 I made it
@stakx yer same as I said above, I agree they should throw for invalid input, but what about the key already existing?
@stakx good point, I don't think we went far enough with moving away from
@stakx The rest of your comments are all valid. With this read-only stuff this pull request was trying to do something that wasn't fully implemented, implementing read-only-ness here currently just confuses things because it isn't supported down the chain. As I mentioned above I've removed it, and it can be restored if/when the rest of the work gets done to support immutable Arguments through the resolution chain, even if we can actually do that.
I think the 3 of us are in agreement on this now, we started with them but they disappeared. I didn't quite like the constructors that accepted a single argument when that was changed, then sort of liked how much simpler it looked, but maybe that was the wrong solution, maybe
True. It'd be great if you could apply params to IEnumerable, I know this has been a long standing missing feature. Will make this change because the compiler does allow both methods declare and can resolve between them. |
With GitHub running today, posting my comments stored away yesterday. I do want to stop going around in circles on this API so we can get this sorted. Please reply to anything useful in my comment above, but I've put together the full contract with some TODOs of things unconfirmed below. I'm hoping the 3 of us can discuss the rest of this, settle on a definition quickly, I'll implement it, and we can get it merged very soon. class Arguments
//TODO: probably don't need this anymore, which methods do we keep?
//TODO: Count, indexer, Add(object, object)??, Clear,
//TODO: Contains(object)?, Contains(str)?, Contains(Type)?, Remove?
// : IDictionary
//TODO: means we don't need Clone, just pass into ctor?
// : IEnumerable<KeyValuePair<object, object>>
{
Arguments() { }
Arguments(IEnumerable<KeyValuePair<object, object>> arguments) { }
Arguments(IEnumerable<DictionaryEntry> arguments) { }
// IEnumerable
IEnumerator GetEnumerator() { return null; }
// ICollection
int Count { get; }
object SyncRoot { get; }
bool IsSynchronized { get; }
void CopyTo(Array array, int index) { }
// IDictionary
object this[object key] { get { return null; } set { } }
ICollection Keys { get { return null; } }
ICollection Values { get { return null; } }
bool IsReadOnly { get { return false; } }
bool IsFixedSize { get { return false; } }
void Add(object key, object value) { }
void Clear() { }
bool Contains(object key) { return false; }
void Remove(object key) { }
// Add instance methods
//TODO: This replaces the IDictionary non-generic overload:
Arguments Add(IEnumerable<DictionaryEntry> arguments) { return null; }
//TODO: Do we even want to suffix these methods with Named/Typed anymore???
Arguments AddNamed(string key, object value) { return null; }
Arguments AddNamed(IEnumerable<KeyValuePair<string, object>> arguments) { return null; }
Arguments AddNamedProperties(object instance) { return null; }
Arguments AddTyped(Type key, object value) { return null; }
Arguments AddTyped<TDependencyType>(TDependencyType value) { return null; }
Arguments AddTyped(IEnumerable<object> arguments) { return null; }
// From factory methods
//TODO: we've already got the same ctor, remove?
static Arguments FromNamed(IEnumerable<KeyValuePair<string, object>> arguments) { return null; }
//TODO: just name it FromProperties since it is obvious they'll be named?
static Arguments FromNamedProperties(object instance) { return null; }
//TODO: probably not commonly used, users can use AddTyped which would also be clearer, remove?
static Arguments FromTyped(IEnumerable<object> arguments) { return null; }
} |
I think I still lean towards
Sure; there's an implied word 'argument' or 'arguments' after these names, so they all make sense to me except for I would remove all interface implementations and methods caused by them. As far as I know it's not a goal for users to use this as a general-purpose dictionary type, right? Interfaces should only be implemented if |
I tried to respond to all TODOs and questions so this post got a bit long. Sorry for that.
Like @jnm2, I don't think that using
I agree that
I suggest to steer clear of a
Note that That being said, I think it would be good to keep the factory method and drop the ctor (because that way you can "name" the ctor, giving some additional context on what the parameter means). (I don't know enough about the
LGTM. It seems good to not call all of those You could possibly rename
I'd say decide on
Same argument ("users can use |
Thanks guys, working through the comments now. I didn't know that var dict = new Dictionary<object, object>();
IDictionary a = dict;
IDictionary<object, object> b = dict;
var c = a["hi"]; // returns null
var d = b["hi"]; // throws KeyNotFoundException Not going to rely on casting to |
new Arguments("parameter", "Hello") // <- they allowed this
new Arguments().AddNamed("parameter", "Hello") // <- you now need to do
Arguments.Named("parameter", "Hello") // <- Just thought of this idea after seeing the
// registration fluent interface again, you could just
// chain .Named and .Typed method calls. It isn't very
// clear though to use as a C# statement and not very
// idiomatic C#, whereas the registration API is always
// used fluently
Probably time we wrap this up, please take another look before we merge. |
This is traditionally just indexer assignment which it looks like you already have: Looks great! |
Oh definitely, and I expect most people would use the indexer for that. I was referring to the fact you can't use it fluently and if someone does really need that ability (which I think won't be often) they can always implement that extension themselves. |
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.
LGTM! Just too minor details I noticed, see review comments.
Resolve overloads that take an object or IDictionary are replaced with an Arguments instance, along with extension methods for IEnumerable<KeyValuePair<string, object>>. The Arguments class has been reworked to more easily add named and typed arguments.
6ed593c
to
49aa6d2
Compare
This pull request addresses my code review comments from the now closed #420, and I've rewritten the docs page for arguments.
Ultimately this was to resolve #346.
@jnm2 if I can get your review on this, I'll get this merged and we can push out a beta for 5.0 with the ASP.NET Core changes.
/cc @jnm2 @mario-d-s @stakx