-
Notifications
You must be signed in to change notification settings - Fork 227
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
Fix S1200: Should not count generic type parameters of extension methods #1180
Comments
To broaden the subject of the issue, primitive types such as I read #548 where If you're inclined to go over this rule again, my current feeling is that all basic generic types should be excluded:
Or, since this list might be too hard to get just right, maybe as an alternative you could only count generic type definitions instead of generic types (obtained through |
As yet another alternative, I have found that the default of 30 that FxCop rule CA1506 applies seems more suitable, nearly all of our classes which have a reasonable level of responsability are under 30. |
Hi @antoinebj, Thanks again (for your other issue) for this feedback! I cannot remember why I have used a default value of 20 (probably because that's the value used by Java). I am not really sure which direction we should go for so I will have a chat with the rest of the team to gather opinions and will get back to you right after so we can discuss about it. |
Alright I did some tests and I have noticed that the implementation is quite buggy (especially around generics). We are working on another topic at the moment but I'll update your ticket (mark it as I have read all your suggestions and here is what I think:
WDYT? |
Thanks for your replies I wanted to get back to you on that earlier, but had little time lately. For future reference, I'd like to illustrate the reason why I think the over-simplistic algorithm of CA1506 should not be used as a basis. Imagine someone coded in C# 1 / .NET 1.0 style, without generics: void DoSomething(IList listOfAs, IList listOfBs, IList listOfStrings)
{
A itemOfTypeA = (A)listOfAs[0];
B itemOfTypeB = (B)listOfBs[0];
string itemOfTypeString = (string)listOfStrings[0];
} CA1506 (and S1200 which mimicks it) would count 3 dependencies: Now let's write the exact same thing C# 2 / .NET 2.0 style: void DoSomething(IList<A> listOfAs, IList<B> listOfBs, IList<string> listOfStrings)
{
A itemOfTypeA = listOfAs[0];
B itemOfTypeB = listOfBs[0];
string itemOfTypeString = listOfStrings[0];
} Boom, now we have 5 dependencies (+66%): It gets worse if we have another method in the class: static A GetFirstItemOfTypeA(IEnumerable<A> collectionOfAs) => collectionOfAs.First(); and make the previous example call it: void DoSomething(IList<A> listOfAs, IList<B> listOfBs, IList<string> listOfStrings)
{
A itemOfTypeA = GetFirstItemOfTypeA(listOfAs);
B itemOfTypeB = listOfBs[0];
string itemOfTypeString = listOfStrings[0];
} Now for the same conceptual object, you have a 6th dependency: Imagine other methods taking So I agree with the 2 points you suggest:
But I'll add a 3rd, which I believe requires consideration:
I may not have thought it entirely through, so I wouldn't be surprised if you find something wrong with that 3rd point. Please do let me know what you think. |
I believe you had a reply about generics, which I received as e-mail notification, but you probably deleted it afterwards. Just in case, I'll address it anyway. Let's take this method (it's inspired from your example from the deleted reply): public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key) {} For that method, I would only count To futher illustrate why I would not count public static object GetValueOrDefault(this HashTable dictionary, object key) {} (
Now if the generic had type constraints: public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
where TKey : Foo
where TValue : Bar
{} Then the non-generic would be: public static Bar GetValueOrDefault(this HashTable dictionary, Foo key) {} So type constraints should count (except In general, I'd say that for generics we can look at how the non-generic version would look like to decide what should really count as a dependency. So, for: public class Class1
{
public void Foo<T>() { }
public void Bar<T>() { }
} I count 2: For: public class Class1
{
public void Foo<T>() where T: class { }
public void Bar<T>() where T: System.Exception { }
} I count 3: |
Description
Violation of rule S1200 is raised excessively for some static classes containing extension methods that extend generic types.
Repro steps
Expected behavior
The analyzer should not request that this class be split, i.e. rule S1200 should not have been violated by that code.
Actual behavior
Each generic type parameter of a method is considered a different dependency for the class. In turn, other generic types using the method's generic types as type parameters are considered as dependencies.
In the above example,
TKey
,TValue
,IDictionary<TKey, TValue>
andFunc<TValue>
are counted as separate dependencies every time they are redefined in a method.This adds up very quickly to 21 in the above example, above the tolerance of 20, and therefore raises a rule violation.
Related information
The text was updated successfully, but these errors were encountered: