Avoid custom delegates
Prefer generic delegates over custom delegates:
- Action<…> for void return type.
- Func<…> for a return type (last generic argument).
- Predicate<T> for a T instance returning a boolean.
- Expression<…> for function definitions compiled/invoked at runtime, also serializable to remote processes.
They clarify code and remove delegate type maintenance. Do not convert delegates consumed by DllImport extern methods (marshalling issues).
- Action<…> for void return type.
- Func<…> for a return type (last generic argument).
- Predicate<T> for a T instance returning a boolean.
- Expression<…> for function definitions compiled/invoked at runtime, also serializable to remote processes.
They clarify code and remove delegate type maintenance. Do not convert delegates consumed by DllImport extern methods (marshalling issues).
How to fix
Replace custom delegates with the generic delegates shown in the replaceWith column.
Estimated Debt: 5 minutes per custom delegate plus 3 minutes per method using it.
Estimated Debt: 5 minutes per custom delegate plus 3 minutes per method using it.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Avoid custom delegates</Name>
// <Id>ND1300:AvoidCustomDelegates</Id>
warnif count > 0
from t in JustMyCode.Types where t.IsDelegate && !t.IsGeneric
let invokeMethod = (from m in t.Methods where m.SimpleNameEquals("Invoke") select m).Single()
let signature1 = invokeMethod.Name.Substring(
invokeMethod.SimpleName.Length,
invokeMethod.Name.Length - invokeMethod.SimpleName.Length)
// 'ref' 'out' and pointers parameters cannot be supported
where !signature1.Contains("&") && !signature1.Contains("*")
let signature2 = signature1.Replace("(","<").Replace(")",">")
let signature3 = signature2 == "<>" ? "" : signature2
let resultTypeName = invokeMethod.ReturnType == null ? "????" :
invokeMethod.ReturnType.FullName == "System.Void" ? "" :
invokeMethod.ReturnType.Name
let replaceWith =
resultTypeName == "Boolean" && invokeMethod.NbParameters == 1 ?
"Predicate" + signature3 : resultTypeName == "" ?
"Action" + signature3 : invokeMethod.NbParameters ==0 ?
"Func<" + resultTypeName + ">" :
"Func" + signature3.Replace(">", "," + resultTypeName + ">")
let methodsUser = t.TypesUsingMe.ChildMethods().Where(m => m.IsUsing(t))
select new {
t,
replaceWith,
methodsUser,
Debt = (5 + 3*methodsUser.Count()).ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *{0}* should be replaced with *{1}*.
// Generic delegates clarify code and
// reduce delegate type maintenance.
//</Expl>
//<Desc>
// Prefer generic delegates over custom delegates:
//
// - *Action<…>* for *void* return type.
//
// - *Func<…>* for a return type (last generic argument).
//
// - *Predicate<T>* for a *T* instance returning a *boolean*.
//
// - *Expression<…>* for function definitions compiled/invoked at runtime,
// also serializable to remote processes.
//
// They clarify code and remove delegate type maintenance. Do not convert
// delegates consumed by *DllImport* extern methods (marshalling issues).
//</Desc>
//<HowToFix>
// Replace custom delegates with the generic delegates shown in the
// **replaceWith** column.
//
// Estimated Debt: 5 minutes per custom delegate plus 3 minutes per method
// using it.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Design: all 16 items
- Avoid custom delegates ND1300
- Types with disposable instance fields must be disposable ND1301
- Disposable types with unmanaged resources should declare finalizer ND1302
- Methods that create disposable object(s) and that don't call Dispose() ND1303
- Disposable Types Must Unsubscribe Events ND1315
- Classes that are candidate to be turned into structures ND1304
- Avoid namespaces with few types ND1305
- Nested types should not be visible ND1306
- Declare types in namespaces ND1307
- Empty static constructor can be discarded ND1308
- Instances size shouldn't be too big ND1309
- Attribute classes should be sealed ND1310
- Don't use obsolete types, methods or fields ND1311
- Do implement methods that throw NotImplementedException ND1312
- Override equals and operator equals on value types ND1313
- Boxing/unboxing should be avoided ND1314