简体   繁体   中英

C# overload resolution with IList<T> and IReadOnlyList<T>

I have a method which I'd like to take all list-like objects in my solution. Before .NET 4.5, this was simple:

public static T Method<T>(IList<T> list)
{
    // elided
}

However, .NET 4.5 introduced IReadOnlyList<T> , which this method should also apply to.

I can't just change the signature to take an IReadOnlyList<T> , as there are places where I apply the method to something specifically typed as an IList<T> .

The algorithm can't run on IEnumerable<T> , and it's used too frequently (and with too large objects) to take an IEnumerable<T> and create a new List<T> on every call.

I've tried adding an overload:

public static T Method<T>(IReadOnlyList<T> list)
{
    // elided
}

... but this won't compile for anything which implements both interfaces ( T[] , List<T> , and numerous other types), as the compiler can't determine which method to use (particularly annoying as they have the same body, so it doesn't matter).

I don't want to have to add overloads of Method which take T[] , and List<T> , and every other type which implements both interfaces.

How should I accomplish this?

This might be one of those occasions where actually checking the runtime type is useful:

public static T Method<T>(IEnumerable<T> source)
{
    if (source is IList<T> list)
        return Method(list);

    if (source is IReadOnlyList<T> readOnly)
        return Method(readOnly);

    return Method(source.ToList() as IList<T>);
}

private static T Method<T>(IReadOnlyList<T> list) { ... }
private static T Method<T>(IList<T> list) { ... }

You still have to duplicate code in the sense that you need seperate implementations for IList and IReadOnlyList because there is no common interface you can leverage, but you at least avoid the ambigous call issue.

Your likely best bet is to do a global search and replace of IList to IReadOnlyList . If there are no compiler errors then you should be fine.

You should only receive compiler errors if you are using IList.Add - which is foolhardy anyway, since arrays don't support Add .

Can you change the code of Method calling? What if you create a method like this:

    public static T1 Method<T1, T2>(T2 list) where T2 : IList<T1>, IReadOnlyList<T1>
    {
        return default(T1);
    }

In this case the calls look like this:

List<string> listA = new List<String>();
ReadOnlyCollection<string> listB = listA.AsReadOnly();

string outVar1 = Method<string, List<string>>(listA);
string outVar2 = Method<string, ReadOnlyCollection<string>>(listB);

Another way to create two extension methods for IList and IReadOnlyList this way:

    public static T Test<T>(this IList<T> source)
    {
        return default(T);
    }

    public static T Test<T>(this IReadOnlyList<T> source)
    {
        return default(T);
    }

And call them like this:

    string outVar1 = (listA as IReadOnlyList<string>).Test();
    string outVar2 = (listB as IList<string>).Test();

Maybe your best solution is to look into why your algorithm can't run on an IEnumerable and change that. Are you using IList<T> or IReadOnlyList<T> -specific members that you could replace with members available in IEnumerable<T> ? Eg:

// instead of
int c = list.Count;

// use
int c = list.Count();

EDIT: ignore the nonsense below. I am leaving it so that the comments continue to make sense.

You should not implement both IList<T> and IReadOnlyList<T> in any class. The only additional members in the IList specification are for writing to the list. You would not need to do that if your list is read only. I think you need to change any classes that implement both so that the correct method can be selected when using them.

However, As all members of IReadOnlyList<T> are included in IList<T> (along with those derived from IReadOnlyCollection<T> ) I wonder if the IList<T> in .Net should actually be changed so that it inherits the IReadOnlyList<T> interface rather than duplicating the members. Not that that helps you now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM