Skip to content

Instantly share code, notes, and snippets.

@ilkerde
Created March 10, 2011 17:26
Show Gist options
  • Save ilkerde/864508 to your computer and use it in GitHub Desktop.
Save ilkerde/864508 to your computer and use it in GitHub Desktop.
What do you think about the type signature of this method ?!?
public IEnumerable<Answer> GetAll()
{
List<Answer> answers = GetAnswers();
return answers;
}
@agross
Copy link

agross commented Mar 10, 2011

@BJörn: Nitpicker! ;-)

@MikeBild
Copy link

Right

@MikeBild
Copy link

GetAll() say to me - do not modify, get only. The type say - do not modify, get only.

@BjRo
Copy link

BjRo commented Mar 10, 2011

Do you mean memoization ala function memoization?

@ilkerde
Copy link
Author

ilkerde commented Mar 10, 2011

@BjRo: thanks for clarifying. I did not use the term "method signature" because I'm aware of the fact that method signatures have relevance for overloading/overriding scenarios. I fear not to have sufficient and profound knowledge as to ultimately declare the term "type signature of a method" as a valid description for "return type of method". Nonetheless, my experience as well as many talks with smart developers I thankfully have had since today indicate me towards a solid viewpoint of regarding "type signature" as an adequate term.

@BjRo
Copy link

BjRo commented Mar 10, 2011

Not arguing against you ^^

@ilkerde
Copy link
Author

ilkerde commented Mar 10, 2011

@agross @MikeBild @BjRo: in this context, "memorization" describes the fact that the finite result list of answers has already been iterated over by the GetAnswers() method, which is indicated by the fact that the (achtung! :-)) type signature of GetAnswers() is List. In this specific case, you can safely put the mutability of the returned type aside.

@ilkerde
Copy link
Author

ilkerde commented Mar 10, 2011

@BjRo: Not arguing against either. I just made a bit of fun by decorating our nice chat with ornamental speech, which effectively is broadly known as "Blafasel" :-)

@agross
Copy link

agross commented Mar 10, 2011

@ilker: Quatschäffchen ;-)

@ilkerde
Copy link
Author

ilkerde commented Mar 10, 2011

@agross: With my humblest and deepest respect for your exceptional expertise regarding the c# language, my innocent fear is, to disrespect your last comment as it motivates me to categorize it as inappropriate answer for the question of this carefully crafted gist.

@lanwin
Copy link

lanwin commented Mar 10, 2011

The most people tend to returning lists without knowing what they doing. They thread a List like an array but infect the caller of a method dose not know anything about the list itself. You can not read from GetAnswers if it creates a new list or if it returns the a list from the impl. class. If it dose the last you have a good chance to be able to break a component with modifying the list from outside.

I also see a lot people doing this:

public List<Order> GetOrders()
{
    return _dataContext.Orders.ToList();
}

Its not only that is collect a lot of memory, it also shows that the developer never understand how IEnumerable is working and what it can do for wonderful things for you.

So my guess is that an function/property which returning a range of items should ever use IEnumerable and only should use List if it explicitly want to give the user the ability to change the internal list if an object. Like Orders.OrderLines.Add(...). If the users needs a list in his code he can simply call GetOrders().ToList().

@MikeBild
Copy link

Totally agree @lanwin. This is the reason for questions. Know the coder the functionality of IEnumerable or not. When I know I can hide list functionality to external callers with the special of lazy evaluation.

@ilkerde
Copy link
Author

ilkerde commented Mar 10, 2011

@lanwin: thank you for your detailed reply. appreciate it! i totally understand your point about the "unknowing" or "careless" usage of List instead of IEnumerable. However, as I mentioned in one of my previous comments, my point here is not about (im)mutability.

The background for my "return type" question is quite specific. GetAnswers already memorizes the complete (finite) set. If you do not feel comfortable with the word "memorize", maybe "materialize" or "copy" will work. Basically, GetAnswers "stores" the complete set in a List (in memory). However, the outer function (GetAll) exposes an IEnumerable. That is, it effectively downcasts the type, rendering the result to a forward-only and immutable representation of the set in question. For now, put the mutability thing aside. I might just have used Anwer[] as return type for GetAnswers as well:

public IEnumerable<Answer> GetAll()
{
    Answer[] answers = GetAnswers();
    return answers;
}

Now everything is readonly and immutable. Now, having left this mutability (readonly / modifiable) aspect behind us, the downcast as well as the memorization aspect remains. The memorization aspect is what I wanted to stress with my question.

One last note: I do have a very strong opinion about this specific scenario as well as the general usage of IEnumerable as return type for finite sets. However, I won't express my opinion (yet), since I don't want to influence anyone with my perspective.

@ilkerde
Copy link
Author

ilkerde commented Mar 11, 2011

It was intentional to phrase my question as an open question. Hoewever, since almost anyone looking at the code concentrates on readonlyness and information hiding, i finally decided to rephrase my question and be more specific:

would you rather expose or hide the memorization of the set of answers?

@MikeBild
Copy link

I would hide the "memorization" of the set of answers with a return of IEnumerable instead of Answers[]. Let your caller decide what he want. (IoC, SRC, SoC) -m2c

@SebastianAchatz
Copy link

IMHO the caller should not be bothered by "memorization" aspect. He should not care. IEnumerable could lead the caller to think about "memorization" aspect. List or even IList are a bit to heavy (overhead) for a set of answers IMO. So give Collection and ReadOnlyCollection a thougt. I personally often use IEnumerable for public service interfaces despite it's not recommended by many people. -m2c

@forki
Copy link

forki commented Mar 11, 2011

Hi,

first of all I wouldn't return IEnumerable here. The materialized vs. lazy question is very important. You have the same discussion in Rx land with "hot" and "cold" observables (http://channel9.msdn.com/Blogs/J.Van.Gogh/Rx-API-in-depth-Hot-and-Cold-observables). IMHO you should reflect this in your signature.

But if you use List or Array you are destroying the whole idea. I would consider a immutable list here.

Immutability is even stronger than the materialized view.
Materialized statement: "I calculated all values for you. Feel free to iterate over the collection as often as you like"
Immutable statement: "I calculated all values for you and they will not change in the future. Feel free to iterate over the collection as often as you like and feel free to store additional information (like checksums)."

That said, there is another interesting point here. Consider the following function:

public IEnumerable<Answer> FilterImportantAnswers(IEnumerable<Answer> allAnswers)
{
    return answers.Where(a => a.Important);
}

I often struggle about such functions in .NET, because it is effectively removing type information, like here:

IEnumerable<Answer> answers = FilterImportantAnswers(myImmutableListOfAnswers);

After applying this filter I lose my concrete type and everything is just IEnumerable.

In Haskell one could use type classes to solve this problem. In C# pseudo code something similar could look like this:

public T<Answer> FilterImportantAnswers(T<Answer> allAnswers) where T allows Where
{
    return answers.Where(a => a.Important);
}

ImmutableList<Answer> answers = FilterImportantAnswers(myImmutableListOfAnswers);

Regards,
Steffen

@forki
Copy link

forki commented Mar 11, 2011

Edit:

Sorry. Immutability is not stronger as IEnumerable shows. But they play together.

Materialized statement: "I calculated all values for you. Feel free to iterate over the collection as often as you like"

Immutable and materialized statement: "I calculated all values for you and they will not change in the future. Feel free to iterate over the collection as often as you like and feel free to store additional information (like checksums)."

@MikeBild
Copy link

@steffen: Nice aspect. Unfortunately we can not do
public T FilterImportantAnswers(T allAnswers) where T allows Where
in C#. :-(

@forki
Copy link

forki commented Mar 11, 2011

That's why I wrote "could" ;-)

@MikeBild
Copy link

@steffen: Sure. I'm sorry that we can't do that in C#. But your answer is a good answer for me "When you should return IEnumerable and when a type with the context of a "materialized" scope." Thx.

@forki
Copy link

forki commented Mar 11, 2011

@mike: That's not only a problem of C#. It seems this is general problem in .NET generics.

@MikeBild
Copy link

@steffen: Yes, you're totally right.

@forki
Copy link

forki commented Mar 11, 2011

There is another point. If you return IEnumerable instead of Array or List you are disabling fast indexed access. Why would you do this?

In general: I don't see any value in returning a base class if you could return the concrete class. But maybe I am wrong.

If you want immutability then just use an immutable data structure.

@ilkerde
Copy link
Author

ilkerde commented Mar 11, 2011

@codefromground thanks for your thoughts. appreciate it. i truly hope that we can dive into topics as this one while having a nice espresso at your site ;) Regarding the ReadonlyCollection: I'm really sorry that my initial example utilized a List. It distracted too much from what i wanted to stress. The second example (using Answer[]) fits more to what i wanted to have opinions for.

@ilkerde
Copy link
Author

ilkerde commented Mar 11, 2011

@forki Thanks indeed for your in-depth and thoughtful comments.
I do believe that I would have never achieved to express the importance of materialization (and even more, preserving the materialization information by type signature) as you did in your comment.

At this point, I'd like to thank all of you for lending your time and brain for my little question. It was very valuable and insightful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment