defining enumerator inline

F

fostandy

Howdy,

I have an enumerator:

public IEnumerator<int> foo()
{
yield return 1;
}

and a method bar that consumes the enumerator thus:

public void bar()
{
IEnumerator<int> x = foo();
...
}

but actually foo() is online used inside of bar(). Is there a way to
inline it, in the same way you would with an anonymous delegate and
such?
 
P

Pavel Minaev

I have an enumerator:

public IEnumerator<int> foo()
{
   yield return 1;

}

and a method bar that consumes the enumerator thus:

public void bar()
{
   IEnumerator<int> x = foo();
   ...

}

but actually foo() is online used inside of bar(). Is there a way to
inline it, in the same way you would with an anonymous delegate and
such?

Unfortunately, no. You can of course define an anonymous delegate
inside bar() with return type of IEnumerable<T>. However, "yield
return" is not allowed in anonymous delegates & lambdas - there is a
special error message for that.

This is an often-requested feature, and I think it's reasonable to
expect that in C# 5.0 timeframe or thereabouts, probably something
similar to "new IEnumerator<int> { yield return 1; }".

In the meantime, I suggest looking closer at convenience methods
provided by class Enumerable. Very often I found that something that,
at first glance, is best written using yield, is in fact shorter done
using Enumerable. For example, instead of:

for (int i = 0; i < N; ++i) { yield return Foo(i); yield return Bar
(i); }

consider doing:

Enumerable.Range(0, N).SelectMany(i => new[] { Foo(i), Bar(i) })

etc.

Another option, for the most impatient, is to move to F# :)
 
P

Pavel Minaev

Which reminds me, another approach, for sequences that don't fit the  
Range/Repeat model:

     IEnumerator<int> x = new [] { 1, 2, 3, 5, 7, 11  

}.AsEnumerable().GetEnumerator();

What's the point of that AsEnumerable() call before GetEnumerator()?
I'd understand it if it was on its own (to prevent someone else
casting it back to an array and writing into it), but an array
enumerator is already one level of abstraction deep enough for that...
 
J

Jeroen Mostert

Pavel said:
Which reminds me, another approach, for sequences that don't fit the
Range/Repeat model:

IEnumerator<int> x = new [] { 1, 2, 3, 5, 7, 11

}.AsEnumerable().GetEnumerator();

What's the point of that AsEnumerable() call before GetEnumerator()?
I'd understand it if it was on its own (to prevent someone else
casting it back to an array and writing into it), but an array
enumerator is already one level of abstraction deep enough for that...

Have you actually tried it without .AsEnumerable()? You might see the
problem then.
 
P

Pavel Minaev

Pavel said:
Which reminds me, another approach, for sequences that don't fit the  
Range/Repeat model:
     IEnumerator<int> x = new [] { 1, 2, 3, 5, 7, 11  
}.AsEnumerable().GetEnumerator();
What's the point of that AsEnumerable() call before GetEnumerator()?
I'd understand it if it was on its own (to prevent someone else
casting it back to an array and writing into it), but an array
enumerator is already one level of abstraction deep enough for that...

Have you actually tried it without .AsEnumerable()? You might see the
problem then.

Yes, I forgot that array implements IList implicitly and IList<T> only
explicitly. Still, I think a simple cast is preferable in this case,
as AsEnumerable() always adds that extra level of indirection.
 
F

fostandy

Unfortunately, no. You can of course define an anonymous delegate
inside bar() with return type of IEnumerable<T>. However, "yield
return" is not allowed in anonymous delegates & lambdas - there is a
special error message for that.

This is an often-requested feature, and I think it's reasonable to
expect that in C# 5.0 timeframe or thereabouts, probably something
similar to "new IEnumerator<int> { yield return 1; }".

Thanks for the answer. It's interesting that you mention this is an
often-requested feature because it does seem useful to me. However, I
have not been able to google this (maybe I am using the wrong terms)
and others have 'suggested' to me that I am doing something wrong if I
ever need to do it.
In the meantime, I suggest looking closer at convenience methods
provided by class Enumerable. Very often I found that something that,
at first glance, is best written using yield, is in fact shorter done
using Enumerable. For example, instead of:

I am actually using the iterator to write asynchronous code in a
synchronous fashion, using the AsyncEnumerator class from the rather
amazing/insane wintellect Power Threading library - hence I think the
yield return model is basically necessary. Thanks to all who suggested
alternatives however.
Another option, for the most impatient, is to move to F# :)

I was weaned on an OO paradigm and while happy to wade in other models
I'm not quite ready to start deep sea diving :) Sadly this will wait
until I have a little more time (and brain capacity).




- Hide quoted text -
 
P

Pavel Minaev

Thanks for the answer. It's interesting that you mention this is an
often-requested feature because it does seem useful to me. However, I
have not been able to google this (maybe I am using the wrong terms)
and others have 'suggested' to me that I am doing something wrong if I
ever need to do it.

Well, as you can see, no-one did that here :)

Some people hate lambdas in general, too, just because they weren't
there in the good old days. It's not a good reason to skip on features
that make code clearer and smaller, though.

F# has that feature built-in, by the way:

seq { yield 1; yield! [2; 3]; for x in xs yield x*x; ... }

and yes, it is very convenient to have in practice, once you start
using it. Well, just like lambdas :)
I am actually using the iterator to write asynchronous code in a
synchronous fashion, using the AsyncEnumerator class from the rather
amazing/insane wintellect Power Threading library - hence I think the
yield return model is basically necessary. Thanks to all who suggested
alternatives however.

Yep, I went and had a look at that, and it seems to be heavily
centered around iterators specifically.
I was weaned on an OO paradigm and while happy to wade in other models
I'm not quite ready to start deep sea diving :) Sadly this will wait
until I have a little more time (and brain capacity).

Gladly, F# is a decent OO language as well (since it fully supports
the .NET object model as it is), and ML legacy means that it has all
the stuff we've got used to (e.g. "while" and "for" loops, mutable
variables, etc). I find that just using it for the extra sugar that it
gives - ADTs, pattern matching, type inference, and yes, "seq" and
"async" blocks - is often worth it already.
 
J

Jeroen Mostert

Pavel said:
Pavel said:
On Mar 6, 9:56 pm, "Peter Duniho" <[email protected]>
wrote:
Which reminds me, another approach, for sequences that don't fit the
Range/Repeat model:
IEnumerator<int> x = new [] { 1, 2, 3, 5, 7, 11
}.AsEnumerable().GetEnumerator();
What's the point of that AsEnumerable() call before GetEnumerator()?
I'd understand it if it was on its own (to prevent someone else
casting it back to an array and writing into it), but an array
enumerator is already one level of abstraction deep enough for that...
Have you actually tried it without .AsEnumerable()? You might see the
problem then.

Yes, I forgot that array implements IList implicitly and IList<T> only
explicitly. Still, I think a simple cast is preferable in this case,
as AsEnumerable() always adds that extra level of indirection.
I'd love to agree with you in the spirit of friendship and harmony (and it's
such a minor point), but I just can't bring myself to do it. :)

The full implementation of .AsEnumerable() is this:

public static IEnumerable<TSource> AsEnumerable<TSource>(this
IEnumerable<TSource> source)
{
return source;
}

This method has only one purpose, and that's to make expressions like this

IEnumerator<int> x = ((IEnumerable<int>) new[] { 1, 2, 3, 5, 7, 11
}).GetEnumerator();

more readable, at which I think it succeeds. It should in any case be
inlined to the same thing -- .AsEnumerable() effectively *is* a simple cast.

Everyone should feel free to use what they think looks best. And no
discussions about how inlining isn't always on, please! :)
 
J

Jeroen Mostert

Peter said:
[...]
This method has only one purpose, and that's to make expressions like
this

IEnumerator<int> x = ((IEnumerable<int>) new[] { 1, 2, 3, 5, 7, 11
}).GetEnumerator();

more readable, at which I think it succeeds. It should in any case be
inlined to the same thing -- .AsEnumerable() effectively *is* a simple
cast.

Actually, it's better than that. Not only is the AsEnumerable() method
likely to be inlined, it doesn't involve an actual cast. The type can
be verified statically, and so it winds up essentially being a NOP. The
casting syntax has to actually cast in the IL, and as far as I can
recall the JITter isn't going to do the type verification and get rid of
the cast (by the time it gets the IL, I think it can't, though don't
hold me to that).
No, you're right (at least for my test case). This particular case is
actually a slight blemish on the compiler, because it emits an unnecessary
castclass instruction which it could easily eliminate through static type
analysis. The jitter can't reasonably be expected to do this, but the
compiler should know better. Optimization in .NET is effectively a game of
"I thought you were optimizing this" between the compiler and the jitter...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top