-
-
Notifications
You must be signed in to change notification settings - Fork 158
Adopt nullable reference types #1029
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
Comments
To stay in the pattern, I think bool TryGetResourceContext(string publicName, out ResourceContext? context);
bool TryGetResourceContext(Type resourceType, out ResourceContext? context); |
Hi @ThomasBarnekow, I'm aware of that pattern, which was introduced before nullable value types and generics existed (so before C# 2.0). Over time, using But Microsoft didn't want to break existing users, so they stuck with it and even made it less painful to use by introducing inline declarations ( Finally, this is not trying to parse or validate anything, it is just a keyed lookup, which makes the pattern not a great fit here. |
Hi @bart-degreed, I agree with your points. I was coming from the naming convention that points to a specific pattern with a specific method signature. Would it be better to avoid the |
I don't see why. The method is not called |
The |
I'm not convinced that's the case. Can you share links that prove your assumptions, ie are there prominent coding guidelines that require developers to use that Let's assume that my earlier example "TryEnrollStudentInCourse(Student, Course)" would post a message to a queue to enroll the student, but silently ignore the operation when the student has already enrolled in that same course, but log a warning when that student is already enrolled in a different course at the same date/time and send an email when the student is not eligible for the course. It would return |
Here are a few links to guidelines, posts, and questions:
Here are a few sample methods, some of which have been added recently with .NET 5 or .NET Core 3.0: I'm just saying that the prefix
|
Good sources, you have convinced me. The pattern appears to be more prominently alive than I thought. So I'm okay with adopting the signature you proposed. I'm a bit torn about whether it should throw on null. I've looked at some framework methods:
I tend to think it should, because looking up "nothing" or "I don't know" is an invalid question. But I don't think it should throw on an empty string. What are your thoughts? Would this have any practical implications for JADNC users that I'm not aware of? /cc @maurei |
Today I started refactoring JADNC signatures to the proposed Try/out pattern, but I find it makes code harder to read and consume. Doing only 4 files, I already stumbled on the following issues. For example: public AttrAttribute TryGetAttributeByPropertyName(string propertyName)
{
return propertyName != null && _fieldsByPropertyName.TryGetValue(propertyName, out ResourceFieldAttribute field) &&
field is AttrAttribute attribute ? attribute : null;
} Becomes: public bool TryGetAttributeByPropertyName(string propertyName, out AttrAttribute attrAttribute)
{
if (propertyName != null && _fieldsByPropertyName.TryGetValue(propertyName, out ResourceFieldAttribute field) &&
field is AttrAttribute attribute)
{
attrAttribute = attribute;
return true;
}
attrAttribute = null;
return false;
} And: public ResourceType TryGetResourceType(string publicName)
{
return publicName != null && _resourceTypesByPublicName.TryGetValue(publicName, out ResourceType resourceType)
? resourceType : null;
} Becomes: public bool TryGetResourceType(string publicName, out ResourceType resourceType)
{
if (publicName == null)
{
resourceType = null;
return false;
}
return _resourceTypesByPublicName.TryGetValue(publicName, out resourceType);
} And consuming code: Type effectiveIdType = idClrType ?? _typeLocator.TryGetIdType(resourceClrType); Becomes: Type effectiveIdType = idClrType;
if (effectiveIdType == null)
{
_typeLocator.TryGetIdType(resourceClrType, out effectiveIdType);
} And: AttrAttribute attribute = resourceType.TryGetAttributeByPublicName(attributeName); Becomes: _ = resourceType.TryGetAttributeByPublicName(attributeName, out AttrAttribute attribute); Note I'm using a discard in the last case, to express that I didn't forget to check the return value. Still, the inline declaration makes it harder to track what variables are in scope. And splitting it up in two lines does not sound appealing either. When switching to nullable reference types, we'll need to use special attributes on the Overall, I find that using this pattern, the need arises to introduce temporary variables and copying data to work around its limitations. It doesn't compose well, as the examples show. This gets worse when writing LINQ statements with So my current thinking is to keep things as they are and not introduce this pattern. Feedback welcome. |
Again, my comment on AttrAttribute attribute = resourceType.TryGetAttributeByPublicName(attributeName); becomes AttrAttribute attribute = resourceType.GetAttributeByPublicName(attributeName); Otherwise, I would expect to see this: if (resourceType.TryGetAttributeByPublicName(attributeName, out AttrAttribute attribute))
{
// Perform some action if successful
} This would be comparable to: AttrAttribute attribute = resourceType.GetAttributeByPublicName(attributeName);
if (attribute is not null)
{
// Perform some action if successful
} |
Why not `GetAttributeByPublicNameOrDefault`
?
…On Mon, 11 Oct 2021 at 14:29, Thomas Barnekow ***@***.***> wrote:
Again, my comment on Try was that it is (widely and recently) used with a
specific pattern (the bool return type and an out parameter). Why not
simply remove the prefix Try from the method names. For example:
AttrAttribute attribute = resourceType.TryGetAttributeByPublicName(attributeName);
becomes
AttrAttribute attribute = resourceType.GetAttributeByPublicName(attributeName);
Otherwise, I would expect to see this:
if (resourceType.TryGetAttributeByPublicName(attributeName, out AttrAttribute attribute))
{
// Perform some action if successful
}
This would be comparable to:
AttrAttribute attribute = resourceType.GetAttributeByPublicName(attributeName);if (attribute is not null)
{
// Perform some action if successful
}
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1029 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABLPDFFBA2ROEAMWVJC2D3UGLRDTANCNFSM5ATJZK7Q>
.
|
Also fine or better! Do we need a prefix or postfix? If the answer is yes, the |
We need to distinguish because we have both @ThomasBarnekow you've made your point on the continued usage of the pattern by Microsoft. I don't disagree with that, but I find it quite inconvenient. So I wonder whether we should conform to an unpleasant pattern, solely because Microsoft does. We're not Microsoft, for example, we don't prefix private static fields with |
@dennisdoomen do you have an opinion on this? Are there standards or conventions I'm unaware of? What would you recommend? |
I don't know enough about your library to provide any specific advice, but I can provide generic advice about what I would expect:
|
Thanks for sharing your insights @dennisdoomen, greatly appreciated. As usual, there is no single right solution, so I'm going to choose on a case-by-case basis. For our resource graph lookups, I believe Find* is the most appropriate fit. |
The plan is to enable nullable reference types and review the nullability of all code (ie choose between nullable vs non-nullable references).
A prerequisite for this is .NET 5 because of its improved unconstrained generics support.
In the process, we'll likely want to make breaking changes to clarify the contract. For example
IResourceContextProvider.GetResourceContext()
today returnsnull
when not found. Due to this loose contract, all callers are required to null-check or possibly face aNullReferenceException
. So instead we'd like to offerResourceContext GetResourceContext()
(throws when not found) andResourceContext? TryGetResourceContext
(returnsnull
when not found).The text was updated successfully, but these errors were encountered: