Background:
I am modifying existing code using the Harmony Library. The existing C# code follows this structure:
public class ToModify
{
public override void Update()
{
foreach (StatusItemGroup.Entry entry in collection)
{
// I am trying to alter an operation at the end of this loop.
}
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry { }
}
Due to the situation, I must modify the IL code that is being generated, to do so I must obtain the MethodInfo of my target operand. This is the target:
IL_12B6: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
Question:
How do I obtain the MethodInfo for the MoveNext method of an enumerator?
What I've tried:
Everything I can think of has yielded null results. This is my most basic attempt:
MethodInfo targetMethod = typeof(IEnumerator<StatusItemGroup.Entry>).GetMethod("MoveNext");
I don't understand why this doesn't work, and I don't know what I need to do to correctly obtain the MethodInfo.
MoveNext is not defined on IEnumerator<T>, but on the non-generic IEnumerator which is inherited by IEnumerator<T>.
Interface inheritance is a little weird in combination with reflection, so you need to obtain the method info directly from the base interface where it's defined:
MethodInfo targetMethod = typeof(System.Collections.IEnumerator).GetMethod("MoveNext");
Using the free LinqPad, I create this with Harmony 2.0 RC2. As you can see did I use a pass-through postfix to change the enumerator and wrap it. There are other ways and I suspect that you actually have an IEnumeration somewhere instead. That would be way easier to patch by using the pass-through postfix directly on the original method that returns the IEnumeration. No need to wrap the enumerator in that case.
But I don't know your full use case, so for now, this is the working example:
void Main()
{
var harmony = new Harmony("test");
harmony.PatchAll();
var group = new StatusItemGroup();
var items = new List<StatusItemGroup.Entry>() { StatusItemGroup.Entry.Make("A"), StatusItemGroup.Entry.Make("B") };
Traverse.Create(group).Field("items").SetValue(items);
var enumerator = group.GetEnumerator();
while(enumerator.MoveNext())
Console.WriteLine(enumerator.Current.id);
}
[HarmonyPatch]
class Patch
{
public class ProxyEnumerator<T> : IEnumerable<T>
{
public IEnumerator<T> enumerator;
public Func<T, T> transformer;
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public IEnumerator<T> GetEnumerator()
{
while(enumerator.MoveNext())
yield return transformer(enumerator.Current);
}
}
[HarmonyPatch(typeof(StatusItemGroup), "GetEnumerator")]
static IEnumerator<StatusItemGroup.Entry> Postfix(IEnumerator<StatusItemGroup.Entry> enumerator)
{
StatusItemGroup.Entry Transform(StatusItemGroup.Entry entry)
{
entry.id += "+";
return entry;
}
var myEnumerator = new ProxyEnumerator<StatusItemGroup.Entry>()
{
enumerator = enumerator,
transformer = Transform
};
return myEnumerator.GetEnumerator();
}
}
public class StatusItemGroup
{
public IEnumerator<Entry> GetEnumerator()
{
return items.GetEnumerator();
}
private List<Entry> items = new List<Entry>();
public struct Entry
{
public string id;
public static Entry Make(string id) { return new Entry() { id = id }; }
}
}
Related
I ran into a weird issue and I'm wondering what I should do about it.
I have this class that return a IEnumerable<MyClass> and it is a deferred execution. Right now, there are two possible consumers. One of them sorts the result.
See the following example :
public class SomeClass
{
public IEnumerable<MyClass> GetMyStuff(Param givenParam)
{
double culmulativeSum = 0;
return myStuff.Where(...)
.OrderBy(...)
.TakeWhile( o =>
{
bool returnValue = culmulativeSum < givenParam.Maximum;
culmulativeSum += o.SomeNumericValue;
return returnValue;
};
}
}
Consumers call the deferred execution only once, but if they were to call it more than that, the result would be wrong as the culmulativeSum wouldn't be reset. I've found the issue by inadvertence with unit testing.
The easiest way for me to fix the issue would be to just add .ToArray() and get rid of the deferred execution at the cost of a little bit of overhead.
I could also add unit test in consumers class to ensure they call it only once, but that wouldn't prevent any new consumer coded in the future from this potential issue.
Another thing that came to my mind was to make subsequent execution throw.
Something like
return myStuff.Where(...)
.OrderBy(...)
.TakeWhile(...)
.ThrowIfExecutedMoreThan(1);
Obviously this doesn't exist.
Would it be a good idea to implement such thing and how would you do it?
Otherwise, if there is a big pink elephant that I don't see, pointing it out will be appreciated. (I feel there is one because this question is about a very basic scenario :| )
EDIT :
Here is a bad consumer usage example :
public class ConsumerClass
{
public void WhatEverMethod()
{
SomeClass some = new SomeClass();
var stuffs = some.GetMyStuff(param);
var nb = stuffs.Count(); //first deferred execution
var firstOne = stuff.First(); //second deferred execution with the culmulativeSum not reset
}
}
You can solve the incorrect result issue by simply turning your method into iterator:
double culmulativeSum = 0;
var query = myStuff.Where(...)
.OrderBy(...)
.TakeWhile(...);
foreach (var item in query) yield return item;
It can be encapsulated in a simple extension method:
public static class Iterators
{
public static IEnumerable<T> Lazy<T>(Func<IEnumerable<T>> source)
{
foreach (var item in source())
yield return item;
}
}
Then all you need to do in such scenarios is to surround the original method body with Iterators.Lazy call, e.g.:
return Iterators.Lazy(() =>
{
double culmulativeSum = 0;
return myStuff.Where(...)
.OrderBy(...)
.TakeWhile(...);
});
You can use the following class:
public class JustOnceOrElseEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> decorated;
public JustOnceOrElseEnumerable(IEnumerable<T> decorated)
{
this.decorated = decorated;
}
private bool CalledAlready;
public IEnumerator<T> GetEnumerator()
{
if (CalledAlready)
throw new Exception("Enumerated already");
CalledAlready = true;
return decorated.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
if (CalledAlready)
throw new Exception("Enumerated already");
CalledAlready = true;
return decorated.GetEnumerator();
}
}
to decorate an enumerable so that it can only be enumerated once. After that it would throw an exception.
You can use this class like this:
return new JustOnceOrElseEnumerable(
myStuff.Where(...)
...
);
Please note that I do not recommend this approach because it violates the contract of the IEnumerable interface and thus the Liskov Substitution Principle. It is legal for consumers of this contract to assume that they can enumerate the enumerable as many times as they like.
Instead, you can use a cached enumerable that caches the result of enumeration. This ensures that the enumerable is only enumerated once and that all subsequent enumeration attempts would read from the cache. See this answer here for more information.
Ivan's answer is very fitting for the underlying issue in OP's example - but for the general case, I have approached this in the past using an extension method similar to the one below. This ensures that the Enumerable has a single evaluation but is also deferred:
public static IMemoizedEnumerable<T> Memoize<T>(this IEnumerable<T> source)
{
return new MemoizedEnumerable<T>(source);
}
private class MemoizedEnumerable<T> : IMemoizedEnumerable<T>, IDisposable
{
private readonly IEnumerator<T> _sourceEnumerator;
private readonly List<T> _cache = new List<T>();
public MemoizedEnumerable(IEnumerable<T> source)
{
_sourceEnumerator = source.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return IsMaterialized ? _cache.GetEnumerator() : Enumerate();
}
private IEnumerator<T> Enumerate()
{
foreach (var value in _cache)
{
yield return value;
}
while (_sourceEnumerator.MoveNext())
{
_cache.Add(_sourceEnumerator.Current);
yield return _sourceEnumerator.Current;
}
_sourceEnumerator.Dispose();
IsMaterialized = true;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public List<T> Materialize()
{
if (IsMaterialized)
return _cache;
while (_sourceEnumerator.MoveNext())
{
_cache.Add(_sourceEnumerator.Current);
}
_sourceEnumerator.Dispose();
IsMaterialized = true;
return _cache;
}
public bool IsMaterialized { get; private set; }
void IDisposable.Dispose()
{
if(!IsMaterialized)
_sourceEnumerator.Dispose();
}
}
public interface IMemoizedEnumerable<T> : IEnumerable<T>
{
List<T> Materialize();
bool IsMaterialized { get; }
}
Example Usage:
void Consumer()
{
//var results = GetValuesComplex();
//var results = GetValuesComplex().ToList();
var results = GetValuesComplex().Memoize();
if(results.Any(i => i == 3))
{
Console.WriteLine("\nFirst Iteration");
//return; //Potential for early exit.
}
var last = results.Last(); // Causes multiple enumeration in naive case.
Console.WriteLine("\nSecond Iteration");
}
IEnumerable<int> GetValuesComplex()
{
for (int i = 0; i < 5; i++)
{
//... complex operations ...
Console.Write(i + ", ");
yield return i;
}
}
Naive: ✔ Deferred, ✘ Single enumeration.
ToList: ✘ Deferred, ✔ Single enumeration.
Memoize: ✔ Deferred, ✔ Single enumeration.
.
Edited to use the proper terminology and flesh out the implementation.
I am trying to understand how to use the IEnumerator interface and what it is used for. I have a class which implements the IEnumerator interface. A string array is passed to the constructor method.
The problem is when I execute the code then the array is not listed properly. It should be doing it in the order "ali", "veli", "hatca" but it’s listed at the console in this order "veli", "hatca" and -1. I am so confused. What am I doing wrong here? Can you please help?
static void Main(string[] args)
{
ogr o = new ogr();
while (o.MoveNext())
{
Console.WriteLine(o.Current.ToString());
}
}
public class ogr: IEnumerator
{
ArrayList array_ = new ArrayList();
string[] names = new string[] {
"ali", "veli", "hatca"
};
public ogr()
{
array_.AddRange(names);
}
public void addOgr(string name)
{
array_.Add(name);
}
int position;
public object Current
{
get
{
if (position >= 0 && position < array_.Count)
{
return array_[position];
}
else
{
return -1;
}
}
}
public bool MoveNext()
{
if (position < array_.Count && position >= 0)
{
position++;
return true;
}
else
{
return false;
}
}
public void Reset()
{
position = 0;
}
}
IEnumerator is quite difficult to grasp at first, but luckily it's an interface you hardly ever use in itself. Instead, you should probably implement IEnumerable<T>.
However, the source of your confusion comes from this line from the IEnumerator documentation:
Initially, the enumerator is positioned before the first element in
the collection. The Reset method also brings the enumerator back to
this position. After an enumerator is created or the Reset method is
called, you must call the MoveNext method to advance the enumerator to
the first element of the collection before reading the value of
Current; otherwise, Current is undefined.
Your implementation has its current position at 0 initially, instead of -1, causing the strange behavior. Your enumerator begins with Current on the first element instead of being before it.
It is pretty rare for people to use that API directly. More commonly, it is simply used via the foreach statement, i.e.
foreach(var value in someEnumerable) { ... }
where someEnumerable implements IEnumerable, IEnumerable<T> or just the duck-typed pattern. Your class ogr certainly isn't an IEnumerator, and shouldn't be made to try to act like one.
If the intend is for ogr to be enumerable, then:
public ogr : IEnumerable {
IEnumerator IEnumerable.GetEnumerator() {
return array_.GetEnumerator();
}
}
I suspect it would be better to be IEnumerable<string>, though, using List<string> as the backing list:
public SomeType : IEnumerable<string> {
private readonly List<string> someField = new List<string>();
public IEnumerator<string> GetEnumerator()
{ return someField.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return someField.GetEnumerator(); }
}
I'm trying to port some code that uses Hashtable to an environment that doesn't have this class. So I thought about not messing with the code and just create my own Hashtable from Dictionary, like this:
public class Hashtable : Dictionary<Object, Object> {
// ...
new public IDictionaryEnumerator GetEnumerator() {
var ie = base.GetEnumerator();
while (ie.MoveNext())
yield return new DictionaryEntry(ie.Current.Key, ie.Current.Value);
}
}
I'm getting this error:
error CS1624: The body of 'System.Collections.Hashtable.GetEnumerator()' cannot be an iterator block because 'System.Collections.IDictionaryEnumerator' is not an iterator interface type
Well, but IDictionaryEnumerator inherits from IEnumerator.
The odd thing is that if I just return (IDictionaryEnumerator)base.GetEnumerator(); the code compiles (but fails during runtime in the foreach loop).
I don't understand what this error is telling me and don't know how to properly implement this.
Iterator blocks are rewritten by the compiler into classes that implement IEnumerable or IEnumerator; the compiler doesn't know how to generate a class that implement IDictionaryEnumerator, so you can't use an iterator block to implement that interface.
A possible workaround is to provide your own implementation of IDictionaryEnumerator:
class Hashtable : Dictionary<object, object>
{
new public IDictionaryEnumerator GetEnumerator()
{
return new DictionaryEnumerator(base.GetEnumerator());
}
struct DictionaryEnumerator : IDictionaryEnumerator
{
private Enumerator _en;
public DictionaryEnumerator(Dictionary<object, object>.Enumerator en)
{
_en = en;
}
public object Current
{
get
{
return Entry;
}
}
public DictionaryEntry Entry
{
get
{
var kvp = _en.Current;
return new DictionaryEntry(kvp.Key, kvp.Value);
}
}
public bool MoveNext()
{
bool result = _en.MoveNext();
return result;
}
public void Reset()
{
throw new NotSupportedException();
}
public object Key
{
get
{
var kvp = _en.Current;
return kvp.Key;
}
}
public object Value
{
get
{
var kvp = _en.Current;
return kvp.Value;
}
}
}
}
I have a requirement where I need to know the calling method to the GetEnumerator().
The best way I could think would be possibly overriding the default behaviour to GetEnumerator to one that I create i.e GetEnumerator([CallerMemberName]string caller = null) but I cannot seem to do this as anything calling it still goes to the original one.
public class MyItems : IEnumerable<string>
{
private List<string> items = new List<string>();
public MyItems()
{
items.Add("One");
items.Add("Two");
items.Add("Three");
items.Add("Four");
items.Add("Five");
items.Add("Six");
}
public IEnumerator<string> GetEnumerator()
{
return items.GetEnumerator();
}
public IEnumerator<string> GetEnumerator([CallerMemberName]string caller = null)
{
var method = caller;
return items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
Example of some calling code could be
private void button1_click(object sender,EventArgs e)
{
MyItems items = new MyItems();
foreach (var item in items)
{
}
}
The aim is that I would want to know for example "button1_click" in the GetEnumerator() method
I don't think it's possible to do exactly what you want to do, since foreach, to my knowledge, always calls the GetEnumerator() without any arguments. However, I see two possibilities to your issue
You can use a StackTrace to get the calling method:
public IEnumerator<string> GetEnumerator()
{
StackTrace stackTrace = new StackTrace();
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);
return items.GetEnumerator();
}
or you can use another method instead of the GetEnumerator() which takes the [CallerMemberName] attribute.
public IEnumerable<string> Iterate([CallerMemberName]string caller = null)
{
Console.WriteLine(caller);
return items;
}
foreach (var myItem in items.Iterate())
{
//..
}
seems like , you need to use StackTrace Class
StackTrace st = new StackTrace();
var fr = st.GetFrames();
if(fr != null && fr.Any() &&fr.Count() >1)
{
MessageBox.Show(fr.ElementAt(1).GetMethod().Name);
}
I can do this:
public class EnumerableTest : System.Collections.IEnumerable
{
System.Collections.IEnumerable data;
public EnumerableTest(System.Collections.IEnumerable d)
{
data = d;
}
public System.Collections.IEnumerator GetEnumerator()
{
foreach (object s in data)
{
yield return s;
}
}
}
But I can't do this?:
public class EnumerableTestString : System.Collections.Generic.IEnumerable<string>
{
System.Collections.Generic.IEnumerable<string> data;
public EnumerableTestString(System.Collections.Generic.IEnumerable<string> d)
{
data = d;
}
public System.Collections.Generic.IEnumerator<string> GetEnumerator()
{
foreach (string s in data)
{
yield return s;
}
}
}
The error I get basically says I am missing the method
public System.Collections.IEnumerator GetEnumerator();
When I change the return type of GetEnumerator() to that, then it tells me I am missing
public System.Collections.Generic.IEnumerator<string> GetEnumerator();
If I try to include both, it tells me I have a duplicate method name.
How can I solve this?
How can I solve this?
You need to use explicit interface implementation to implement at least one of the GetEnumerator methods, usually the non-generic one.
The code is simply with using directives :)
using System.Collections;
using System.Collections.Generic;
public class EnumerableTestString : IEnumerable<string>
{
private IEnumerable<string> data;
public EnumerableTestString(IEnumerable<string> d)
{
data = d;
}
public IEnumerator<string> GetEnumerator()
{
foreach (string s in data)
{
yield return s;
}
}
// Explicit interface implementation for non-generic IEnumerable
public IEnumerator IEnumerable.GetEnumerator()
{
// Delegate to the generic version
return GetEnumerator();
}
}
Create both - e.g. an Explicit implementation that will call the Implicit implementation.
Example:
public class EnumerableTestString : System.Collections.Generic.IEnumerable<string>
{
System.Collections.Generic.IEnumerable<string> data;
public EnumerableTestString(System.Collections.Generic.IEnumerable<string> d)
{
data = d;
}
public System.Collections.Generic.IEnumerator<string> GetEnumerator()
{
foreach (string s in data)
{
yield return s;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Implement System.Collections.Generic.IEnumerable<T> instead.
When you do so using VS's smart tag, VS automatically creates two methods. One for the generic implementation and one for the non-generic one (the one you have already) because IEnumerable<T> requires you to implement IEnumerable as well.
public class StringData : IEnumerable<string>
{
...
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
foreach (string s in data) {
yield return s;
}
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator(); // Calls generic GetEnumerator
}
#endregion
}
Note that the non generic method is implemented explicitly and can therefore only be accessed through the interface (IEnumerable x = ...; x.GetEnumerator()). Its purpose is to increase the compatibility and is a remnant of pre-generic c# versions (1.0, 1.1).