I recently had an ugly bug whereby my collection unexpectedly had a reverse order.
public class WidgetContainer : IWidgetContainer
{
    // ...
    public void Add(IWidget widget, int? index = null)
    {
        if (!index.HasValue)
            _collection.Add(widget);
        else
            _collection.Insert(index.Value, widget);
    }
}
The calling code was not specifying the index. So, for all intents and purposes, this code should have been working, inserting elements sequentially.
But it didn't.
Then I looked at the interface:
public interface IWidgetContainer
{
    void Add(IWidget widget, int? index = 0);
}
Boom.
The calling code resolved the instance by interface, so 0 was used instead of null.
No compiler errors, no warnings - nothing. Can I enable them somewhere?
If not, can I automatically detect and prevent such issues, possibly with a solution test? Mono.Cecil, Reflection are all acceptable.
Applying to an assembly:
Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Where(t => t.IsClass)
    .Select(GetDefaultParameterValuesMismatch)
    .Where(m => m.Count() > 0);
IEnumerable<(string Interface, string Class, string Method, string Parameter, object InterfaceParameterValue, object ClassParameterValue)>
    GetDefaultParameterValuesMismatch(Type type)
{
    var interfaceParameterValues = type
        .GetTypeInfo()
        .ImplementedInterfaces
        .SelectMany(i => i.GetMethods().Select(m => new { Type = i.Name, m }))
        .SelectMany(t => t.m.GetParameters().Select(p => new
        {
            Type = t.Type,
            Method = t.m.Name,
            Parameter = p.Name,
            p.DefaultValue
        }));
    var classParameterValues = type
        .GetTypeInfo()
        .GetMethods()
        .SelectMany(m => m.GetParameters().Select(p => new
        {
            Type = type.Name,
            Method = m.Name,
            Parameter = p.Name,
            p.DefaultValue
        }));
    return interfaceParameterValues
            .Zip(classParameterValues, (t1, t2) => new { t1, t2 })
            .Where(typePair => !object.Equals(typePair.t1.DefaultValue, (typePair.t2.DefaultValue)))
            .Select(typePair => (Interface: typePair.t1.Type,
                          Class: typePair.t2.Type,
                          Method: typePair.t1.Method,
                          Parameter: typePair.t1.Parameter,
                          InterfaceParameterValue: typePair.t1.DefaultValue,
                          ClassParameterValue: typePair.t2.DefaultValue));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With