Google C# style guide reasoning is unclear

297 Views Asked by At

I found Google C# style guide and these points

For inputs use the most restrictive collection type possible, for example IReadOnlyCollection / IReadOnlyList / IEnumerable as inputs to methods when the inputs should be immutable.

For outputs, if passing ownership of the returned container to the owner, prefer IList over IEnumerable. If not transferring ownership, prefer the most restrictive option.

about collections I can't understand.

  1. a) What do they mean about most restrictive collection? I suppose that they imply inheritance of interfaces that goes as follows: IReadOnlyList<T> inherits IReadOnlyCollection<T> which inherits IEnumerable<T>. So IEnumerable is the most restrictive one?

    b) And most importantly why does it matter? At any time anyone can cast above-mentioned collections to List<T> and change contents - I see no reason to choose "most restrictive option"

  2. Do they require to return only interfaces? Does it mean that I must always cast my List<T> to IEnumerable<T> - isn't it misleading or hitting performance?

  3. They mention transferring ownership. When do I want to tranfer ownership and when I do not want to do that? And if I do why

2

There are 2 best solutions below

0
On BEST ANSWER

1a) The most restrictive collection is indeed IEnumerable<T>, as it offers the fewest operations. It's a good practice to accept the most restrictive collection possible as an input, as it provides the most freedom to your callers. It also makes it easier to reason about your internal implementation, as you know that the collection has fewer degrees of freedom, e.g. it is easier to reason about immutable collections.

1b) This is not true. An IEnumerable<T> is not necessarily a List. It could also be a custom collection that just happens to implement the interface, and many other things. Your cast is very likely to fail.

2 ) If you return a collection, but you still own it, you want to restrict the caller to perform only the operations you want on the collection. If you want to keep check on the contents of the collection, you shouldn't allow the caller to write to it. Also, you can return a List<T> from a method whose return type is IEnumerable<T> without a cast.

3 ) Transferring ownership basically means that the caller to which you've returned the collection is now responsible for it from now on. You (the callee) are not going to care who adds to the collection or take any responsibility for deallocating it again. In this case it is appropriate to give the caller full access by exposing the collection as its actual type rather than a more restrictive abstraction.

0
On

If you passing ownership - you basically a factory, which creates some collection, which lifetime is controlled by invoker:

public class A : IA
{
    public IList<int> Create(){return new List<int>();}
}

If you don't pass ownership, you are free to, for example, provide same collection for multiple invokers, so caching is possible at practically zero costs:

public class A : IA
{
    private List<int> _someCollection = new List<int>();
    public IEnumerable<int> Create(){return _someCollection; }
}

If you pass IList instead, then no one protects this IList anymore from accidental modifications outside of factory, which will lead to corrupted cache.

About interfaces...

Google inspires you to use interfaces, instead of concrete types, because there is such things as Unit Testing and good refactoring practices, which is practically unmanagable if you work with concrete types.

For example, if I want to test scenario where there is no more RAM on this concrete method invoke, or collection fails to enumerate after say 6-th element how would you test it? Or I want my collection to work best at retrieval of first 3 elements? I would just implement interface:

public class MyOverflowAndPerformantList : IList<int>{/*some bad implementation here*/}

In case of concrete types, you will actually blow up your memory, or use some weird hacks to go around, thus, it is impractical.