Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect default parameter values mismatch between the interface and implementing classes?

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.

like image 293
wh1t3cat1k Avatar asked Oct 27 '25 08:10

wh1t3cat1k


1 Answers

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));
}
like image 129
Andriy Tolstoy Avatar answered Oct 28 '25 22:10

Andriy Tolstoy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!