Get Constructor Matching Parameters - c#

I have a method which takes in a constructor arguments as
Expression<func<T>>
thus declared as
object[] _constructorArgs = () => new MyClass("StringParam", 56, "ThirdParam");
I would instantiate this object as;
Activator.CreateInstance(typeof(MyClass), _constructorArgs);
Which works fine, but it's a bit slow.
Is there a way I can gather the types Constructor based on the contents of _constructorArgs so I would be able to call
ConstructorInfo.Invoke(_constructorArgs);
?
I know the Invoke is possible this way, it's just about finding the correct constructor based on the parameters in _constructorArgs.
Edit - For clarity
Apologies, I was very tired when I first asked this and should have given it more thought.
What I am doing is the following;
public object Create<T>(Expression<Func<T>> constructor)
{
//I didn't include this step in the original code
var constructorArguments =
(((NewExpression)constructor.Body).Arguments.Select(
argument => Expression.Lambda(argument).Compile().DynamicInvoke())).ToArray();
object[] _args = constructorArguments;
return Activator.CreateInstance(typeof(T), _args);
}
However, if I do the following instead;
ConstructorInfo c = type.GetConstructors().FirstOrDefault();
//Get the types constructor
return c.Invoke(_args);
I get better performance, I'm talking the first taking about 2800 milliseconds over a million iterations, using Invoke bringing that down to about 1000 milliseconds, thus 2.8 times faster.
This will work great if the first constructor always matches the arguments given, but this won't always be the case.
I want to know how I can get the correct ConstructorInfo based on arguments given.

If a million new objects per second isn't fast enough for you, you're going to have to go deeper. You need to start caching things. The simplest thing to cache is the constructor itself, so that you don't have to search for the correct constructor all the time. However...
Why are you doing this? Why don't you simply call the lambda outright? You've got all the code to instantiate the class, and then you throw it away and use Activator.CreateInstance? Why? Even if you do that, you don't need to search for the constructor - NewExpression.Constructor has the ConstructorInfo you need. Just do
((NewExpression)constructor.Body).Constructor.Invoke(_args)
and you're done, no searching needed. All the metadata is already there in the expression tree.
Please explain why you can't simply do return constructor(); (with caching if possible / needed - it's handy to pass things as lambda parameters, since you can then easily cache the method itself).

Activator.CreateInstance() under the hood calls GetConstructors() and iterates over them to find the matching ones. This would explain the difference in performance - and if you roll your own implementation chances are you will end up with the same or worse performance.
You could simplify the process by comparing types using parameterType.IsAssignableFrom(argType), and return the first match - you may end up using a different constructor than Activator.CreateInstance() though because it uses the best match, not the first match:
class DerivedClass : BaseClass { }
class Test
{
public Test(BaseClass c)
{
}
public Test(DerivedClass c)
{
}
}
// Uses the most specific constructor, Test(DerivedClass):
Activator.CreateInstance(typeof(Test), new DerivedClass());

Might be a bit late, but I found a nice implementation with caching, which claims to be 70 times faster thant Activator.CreateInstance;
this.Item = (T)Activator.CreateInstance(typeof(T), new Object[] { x, y }, null); // Classical approach
this.Item = Constructor<Func<int,int,T>>.Ctor(x,y); // Dynamic constructor approach
The complete implementation can be found here:
http://www.cyberforum.ru/blogs/32756/blog2078.html

Related

How to use existing method instead of lambda when it's not static?

This must be a duplicate but i haven't found it. I've found this question which is related since it answers why it's recommended to use a method group instead of a lambda.
But how do i use an existing method group instead of a lambda if the method is not in the current class and the method is not static?
Say i have a list of ints which i want to convert to strings, i can use List.ConvertAll, but i need to pass a Converter<int, string> to it:
List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(i => i.ToString());
This works, but it creates an unnecessary anonymous method with the lambda. So if Int32.ToString would be static and would take an int i could write:
List<string> strings = ints.ConvertAll<string>(Int32.ToString);
But that doesn't compile - of course. So how can i use a method group anyway?
If i'd create an instance method like this
string FooInt(int foo)
{
return foo.ToString();
}
i could use strings = ints.ConvertAll<string>(FooInt);, but that is not what i want. I don't want to create a new method just to be able to use an existing.
There is an static method in the framework, that can be used to convert any integrated data type into a string, namely Convert.ToString:
List<int> ints = new List<int> { 1 };
List<string> strings = ints.ConvertAll<string>(Convert.ToString);
Since the signature of Convert.ToString is also known, you can even eliminate the explicit target type parameter:
var strings = ints.ConvertAll(Convert.ToString);
This works. However, I'd also prefer the lambda-expression, even if ReSharper tells you something different. ReSharper sometimes optimizes too much imho. It prevents developers from thinking about their code, especially in the aspect of readability.
Update
Based on Tim's comment, I will try to explain the difference between lambda and static method group calls in this particular case. Therefor, I first took a look into the mscorlib disassembly to figure out, how int-to-string conversion exactly works. The Int32.ToString method calls an external method within the Number-class of the System namespace:
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), SecuritySafeCritical]
public string ToString(IFormatProvider provider)
{
return Number.FormatInt32(this, null, NumberFormatInfo.GetInstance(provider));
}
The static Convert.ToString member does nothing else than calling ToString on the parameter:
[__DynamicallyInvokable]
public static string ToString(int value)
{
return value.ToString(CultureInfo.CurrentCulture);
}
Technically there would be no difference, if you'd write your own static member or extension, like you did in your question. So what's the difference between those two lines?
ints.ConvertAll<string>(i => i.ToString());
ints.ConvertAll(Convert.ToString);
Also - technically - there is no difference. The first example create's an anonymous method, that returns a string and accepts an integer. Using the integer's instance, it calls it's member ToString. The second one does the same, with the exception that the method is not anonymous, but an integrated member of the framework.
The only difference is that the second line is shorter and saves the compiler a few operations.
But why can't you call the non-static ToString directly?
Let's take a look into the ConvertAll-method of List:
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
{
if (converter == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
}
List<TOutput> list = new List<TOutput>(this._size);
for (int i = 0; i < this._size; i++)
{
list._items[i] = converter(this._items[i]);
}
list._size = this._size;
return list;
}
The list iteraterates over each item, calls the converter with the item as an argument and copys the result into a new list which it returns in the end.
So the only relation here is your converter that get's called explicitly. If you could pass Int32.ToString to the method, the compiler would have to decide to call this._items[i].ToString() within the loop. In this specific case it would work, but that's "too much intelligence" for the compiler. The type system does not support such code conversions. Instead the converter is an object, describing a method that can be called from the scope of the callee. Either this is an existing static method, like Convert.ToString, or an anonymous expression, like your lambda.
What causes the differences in your benchmark results?
That's hard to guess. I can imagine two factors:
Evaluating lambdas may result in runtime-overhead.
Framework calls may be optimized.
The last point especially means, that the JITer is able to inline the call which results in a better performance. However, those are just assumptions of mine. If anyone could clarify this, I'd appreciate it! :)
You hit the nail on the head yourself:
This works, but it creates an unnecessary anonymous method with the
lambda.
You can't do what you're asking for because there is no appropriate method group that you can use so the anonymous method is necessary. It works in that other case because the implicit range variable is passed to the delegate created by the method group. In your case, you need the method to be called on the range variable. It's a completely different scenario.

Get best matching overload from set of overloads

Let's say I have a class as follows:
public class AcceptMethods
{
public int Accept(string s, int k = 1)
{
return 1;
}
public int Accept(object s)
{
return 2;
}
public int Accept(IEnumerable<object> s)
{
return 7;
}
public int Accept(IList<object> s)
{
return 4;
}
}
Now, if I try to consume this in code, I use something like this:
object[] list = new object[] { "a", new object[0], "c", "d" };
Assert.AreEqual(7, list.Select((a)=>((int)new AcceptMethods().Accept((dynamic)a))).Sum());
The reason that it's 7, is because overload resolution prefers [IList<object>] over [IEnumerable<object>] and [object], and because [string, int=default] has preference over [object].
In my scenario, I'd like to get the best matching overload using reflection. In other words: 'best' is defined as 'c# overload resolution'. E.g.:
int sum = 0;
foreach (var item in list)
{
var method = GetBestMatching(typeof(AcceptMethods).GetMethods(), item.GetType());
sum += (int)method.Invoke(myObject, new object[]{item});
}
Assert.AreEqual(7, sum);
While the scenario I sketch has only 1 parameter, the solution I seek can have multiple parameters.
Update 1:
Because I received a comment that this is too difficult for SO due to the difficulties of overload resolution implementation (which I am well aware of), I feel inclined to send an update. To give my argument some power, this was my first attempt, which uses the default .NET binder that handles the overload resolution:
private MethodBase GetBestMatching(IEnumerable<MethodInfo> methods, Type[] parameters)
{
return Type.DefaultBinder.SelectMethod(BindingFlags.Instance | BindingFlags.Public | BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod,
methods.ToArray(), parameters, null);
}
This version already seems to do simple overload resolution correctly, but fails to work with optional parameters. Because .NET afaik works with type binding like I show here, I suppose that the solution could be implemented fairly easy.
This is a massive subject, requires quite a lot of work and certainly cannot be wrapped up in an SO answer in my opinion. I suggest you read through the C# spec and read the formal rules defining overload resolution (also, please pay attention to generic methods) and try to implement them up to the point that satisfies your needs.
Update
Optional (i.e. parameters with default values) are not a trivial case - and the Reflection binder makes no attempt at all to fill them in - that's because it's a compiler's job to identify the default values, pull them out and inject them into a call to such a method.
You need a multi-pass approach that's something like this (note - does NOT include generics):
Search manually for a method whose number of parameters and types of those parameters match exactly the number and types of arguments you've got. If you find a match - use it and bunk out.
Now identify the 'candidate list' of methods for your overload selection (generally that's by name - you might also exclude generics here - unless you're going to try and bind those too).
If none of those methods have optional parameters then you might be able to go ahead and use the default binder as per your question to find the match (if not, you need a argument/parameter-ranking algorithm based on the types - if so, skip to 5).
Re-running through the candidate list built in 3), pull out all the optional parameters and incorporate their default values into your own parameter lists (you might need to build a separate set of parameters for each method at this point, including those that you have been supplied, but also the defaults).
Run your ranking algorithm for these methods built in 3) and possibly 4) to identify the best match (you seem to have a good handle on this so I'm not going to go through it all here - it's not a simple algorithm and I frankly can't quote it all verbatim here either!).
Your ranking algorithm should produce a clear winning method - i.e. with a unique high score or similar. If you get a clear winner, then that's the one you bind. Otherwise you have an ambiguity and you have to bunk out.
You might be interested in my own SO at this point - Default parameters and reflection: if ParameterInfo.IsOptional then is DefaultValue always reliable? - which should help you with identifying methods that have parameters with defaults, and how to lift them out.
For other people that want to do runtime overload resolution, this is a fairly complete description on how to implement it.
Important side note is that the 'dynamic' trick doesn't work in all scenario's as well (specifically: generics); it seems that the compiler is more flexible than runtime behavior.
Also note that this is not a complete algorithm/implementation (or at least I think that it isn't), but works in most cases including nevertheless. I found this to work in all cases that I encountered so far, including difficult cases like array covariance.
The scoring algorithm works as follows:
If parameter type == source type: score = 0
If the parameter is a generic type argument that is valid (generic constraints): score = 1
If the source type is implicitly convertible to the parameter type: score = 2 (see: http://msdn.microsoft.com/en-us/library/y5b434w4.aspx for all rules)
If you need to fill in a default parameter: score = 3
Otherwise calculate the score for compatibility score as below
The compatibility score is the most strict conversion between a type type A and type B (including and covariance, contravariance). For example, string[] has 1 conversion to IList (using object[] and then IList) and 2 conversions to IEnumerable (1. by using object[] and then IEnumerable or 2. by IEnumerable). Therefore, IList is the more strict conversion and is therefore selected.
Counting the number of conversions is easy:
int cnt = CountCompatible(parameter.ParameterType, sourceType.GetInterfaces()) +
CountCompatible(parameter.ParameterType, sourceType.GetBaseTypes()) +
CountCompatible(parameter.ParameterType, new Type[] { sourceType });
[...]
private static int CountCompatible(Type dst, IEnumerable<Type> types)
{
int cnt = 0;
foreach (var t in types)
{
if (dst.IsAssignableFrom(t))
{
++cnt;
}
}
return cnt;
}
To make sure that a better score is selected when a more strict conversion is used, I calculate score as 'score = 5 - 1.0 / (cnt + 2);'. The +2 ensures that you never divide by 0 or 1, leading to a score between 4 and 5.
To do overload resolution, select the method with the minimum score for all arguments. Make sure you enter default method arguments properly when invoking (see the excellent link by Andras above) and make sure you fill in the generic arguments before you return the method. If you encounter a draw for the best method: the best resolution is to throw an exception.
In case you wonder, yes... it's quite a bit of work to get it all working correctly... I plan to make a working version available in my framework once that's done. (You'll see the moment my profile has a working website link :-) )

Can I avoid or optimize this dynamic invocation?

I'm trying to write a class which can handle many different types of inputs, all of which implement the same interface.
I have the following code:
private IEnumerable<IPlan> DevisePlans(ITile tile, IEnumerable<ISpace> spaces)
{
MethodInfo method = GetType().GetMethod("DevisePlans",
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new[] {tile.GetType(), typeof(ISpace)},
null);
var type = typeof(Func<,,>).MakeGenericType(tile.GetType(), typeof(ISpace), typeof(IEnumerable<IPlan>));
var planner = Delegate.CreateDelegate(type, this, method);
return spaces.SelectMany(s => (IEnumerable<IPlan>)planner.DynamicInvoke(tile, s));
}
There are lots of various implementations of DevisePlans within the class, each with a first parameter type that implements ITile.
private IEnumerable<IPlan> DevisePlans(Foo tile, ISpace space) { /* ... */ }
private IEnumerable<IPlan> DevisePlans(Bar tile, ISpace space) { /* ... */ }
This works, but I'm calling DynamicInvoke for every single iteration of my enumerable. Even if I can't completely avoid the dynamic call, is there any way to optimize this method so that the dynamic invocation no longer resides within my loop?
It looks like you are using this to call the most specific overload (not override) of DevisePlans for the tile provided. Assuming my understanding is correct (please do tell me if I'm wrong), then just use dynamic - it has an inbuilt cache and is optimized for this:
return spaces.SelectMany(s =>
(IEnumerable<IPlan>)DevisePlans((dynamic)tile, (dynamic)s)
);
and... that's it!
I would, however, be tempted to look for an answer involving either polymorphism (against ITile) or C# 4.0 variance.
Create an expression tree (start with Expression.Call), compile it, and you'll have a fast delegate. You should cache the delegate based on the type so that you don't compile for the same type multiple times.
Think what you're looking for here is the visitor pattern. Define an ITileVisitor interface with your overload for each type and then ITile has a Visit method which takes the visitor as a parameter. Its the implementation of Visit which will cause the correct Devise overload to be called.

Why would adding a method add an ambiguous call, if it wouldn't be involved in the ambiguity

I have this class
public class Overloaded
{
public void ComplexOverloadResolution(params string[] something)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
}
If I call it like this:
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
It writes Normal Winner to the console.
But, if I add another method:
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
I get the following error:
The call is ambiguous between the following methods or properties: > 'Overloaded.ComplexOverloadResolution(params string[])' and 'Overloaded.ComplexOverloadResolution<string>(string)'
I can understand that adding a method might introduce a call ambiguity, but it's an ambiguity between the two methods that already existed (params string[]) and <string>(string)! Clearly neither of the two methods involved in the ambiguity is the newly added method, because the first is a params, and the second is a generic.
Is this a bug? What part of the spec says that this should be the case?
Is this a bug?
Yes.
Congratulations, you have found a bug in overload resolution. The bug reproduces in C# 4 and 5; it does not reproduce in the "Roslyn" version of the semantic analyzer. I've informed the C# 5 test team, and hopefully we can get this investigated and resolved before the final release. (As always, no promises.)
A correct analysis follows. The candidates are:
0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string)
3: C(string, object)
Candidate zero is obviously inapplicable because string is not convertible to string[]. That leaves three.
Of the three, we must determine a unique best method. We do so by making pairwise comparisons of the three remaining candidates. There are three such pairs. All of them have identical parameter lists once we strip off the omitted optional parameters, which means that we have to go to the advanced tiebreaking round described in section 7.5.3.2 of the specification.
Which is better, 1 or 2? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 1. So 2 cannot be the winner.
Which is better, 1 or 3? The relevant tiebreaker is: a method applicable only in its expanded form is always worse than a method applicable in its normal form. Therefore 1 is worse than 3. So 1 cannot be the winner.
Which is better, 2 or 3? The relevant tiebreaker is that a generic method is always worse than a non-generic method. 2 is worse than 3. So 2 cannot be the winner.
To be chosen from a set of multiple applicable candidates a candidate must be (1) unbeaten, (2) beat at least one other candidate, and (3) be the unique candidate that has the first two properties. Candidate three is beaten by no other candidate, and beats at least one other candidate; it is the only candidate with this property. Therefore candidate three is the unique best candidate. It should win.
Not only is the C# 4 compiler getting it wrong, as you correctly note it is reporting a bizarre error message. That the compiler is getting the overload resolution analysis wrong is a little bit surprising. That it is getting the error message wrong is completely unsurprising; the "ambiguous method" error heuristic basically picks any two methods from the candidate set if a best method cannot be determined. It is not very good at finding the "real" ambiguity, if in fact there is one.
One might reasonably ask why that is. It is quite tricky to find two methods that are "unambigously ambiguous" because the "betterness" relation is intransitive. It is possible to come up with situations where candidate 1 is better than 2, 2 is better than 3, and 3 is better than 1. In such situations we cannot do better than picking two of them as "the ambiguous ones".
I would like to improve this heuristic for Roslyn but it is a low priority.
(Exercise to the reader: "Devise a linear-time algorithm to identify the unique best member of a set of n elements where the betterness relation is intransitive" was one of the questions I was asked the day I interviewed for this team. It's not a very hard algorithm; give it a shot.)
One of the reasons why we pushed back on adding optional arguments to C# for so long was the number of complex ambiguous situations it introduces into the overload resolution algorithm; apparently we did not get it right.
If you would like to enter a Connect issue to track it, feel free. If you just want it brought to our attention, consider it done. I'll follow up with testing next year.
Thanks for bringing this to my attention. Apologies for the error.
What part of the spec says that this should be the case?
Section 7.5.3 (overload resolution), along with sections 7.4 (member lookup) and 7.5.2 (type inference).
Note especially section 7.5.3.2 (better function member), which says in part "optional parameters with no corresponding arguments are removed from the parameter list," and "If M(p) is a non-generic method amd M(q) is a generic method, then M(p) is better than M(q)."
However, I do not understand these parts of the spec thoroughly enough to know which parts of the spec control this behavior, let alone to judge whether it is compliant.
you can avoid this ambiguity by changing the name of the first parameter in some methods and specifying the parameter which you want to assign
like this :
public class Overloaded
{
public void ComplexOverloadResolution(params string[] somethings)
{
Console.WriteLine("Normal Winner");
}
public void ComplexOverloadResolution<M>(M something)
{
Console.WriteLine("Confused");
}
public void ComplexOverloadResolution(string something, object somethingElse = null)
{
Console.WriteLine("Added Later");
}
}
class Program
{
static void Main(string[] args)
{
Overloaded a = new Overloaded();
a.ComplexOverloadResolution(something:"asd");
}
}
If you remove the params from your first method, this wouldn't happen. You first and third method have both valid calls ComplexOverloadResolution(string), but if your first method is public void ComplexOverloadResolution(string[] something) there will be no ambiguity.
Supplying value for a parameter object somethingElse = null makes it optional parameter and thus it doesn't have to be specified when calling that overload.
Edit: The compiler is doing some crazy stuff here. If you move your third method in code after your first, it will report correctly. So it seems it's taking the first two overloads and report them, without checking for the correct one.
'ConsoleApplication1.Program.ComplexOverloadResolution(params string[])' and
'ConsoleApplication1.Program.ComplexOverloadResolution(string, object)'
Edit2: New finding. Removing any method from the above three will produce no ambiguaty between the two. So it seems the conflict only appears if there are three methods present, regardless of order.
If you write
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
or just write
var blah = new Overloaded();
blah.ComplexOverloadResolution();
it will ends up into the same method, in method
public void ComplexOverloadResolution(params string[] something
This is cause params keyword that makes from it it best match also for case when no parameter specified
If you try to add yuor new method like this
public void ComplexOverloadResolution(string something)
{
Console.WriteLine("Added Later");
}
It will perfectly compile and call this method as it is a perfect match for your call with a string parameter. Much stronger then params string[] something.
You declare second method like you did
public void ComplexOverloadResolution(string something, object something=null);
Compiler, jumps in total confusion between the first method and this, just added one.
Cause it doesn't know which function he should all now on your call
var blah = new Overloaded();
blah.ComplexOverloadResolution("Which wins?");
Infact, if you remove string parameter from the call, like a following code, everything compiles correctly and works like before
var blah = new Overloaded();
blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.

Caching delegate results

I have a C# method which accepts a Predicate<Foo> and returns a list of matching items...
public static List<Foo> FindAll( Predicate<Foo> filter )
{
...
}
The filter will often be one of a common set...
public static class FooPredicates
{
public static readonly Predicate<Foo> IsEligible = ( foo => ...)
...
}
...but may be an anonymous delegate.
I'd now like to have this method cache its results in the ASP.NET cache, so repeated calls with the same delegate just return the cached result. For this, I need to create a cache key from the delegate. Will Delegate.GetHashCode() produce sensible results for this purpose? Is there some other member of Delegate that I should look at? Would you do this another way entirely?
To perform your caching task, you can follow the other suggestions and create a Dictionary<Predicate<Foo>,List<Foo>> (static for global, or member field otherwise) that caches the results. Before actually executing the Predicate<Foo>, you would need to check if the result already exists in the dictionary.
The general name for this deterministic function caching is called Memoization - and its awesome :)
Ever since C# 3.0 added lambda's and the swag of Func/Action delegates, adding Memoization to C# is quite easy.
Wes Dyer has a great post that brings the concept to C# with some great examples.
If you want me to show you how to do this, let me know...otherwise, Wes' post should be adequate.
In answer to your query about delegate hash codes. If two delegates are the same, d1.GetHashCode() should equal d2.GetHashCode(), but I'm not 100% about this. You can check this quickly by giving Memoization a go, and adding a WriteLine into your FindAll method. If this ends up not being true, another option is to use Linq.Expression<Predicate<Foo>> as a parameter. If the expressions are not closures, then expressions that do the same thing should be equal.
Let me know how this goes, I'm interested to know the answer about delegate.Equals.
Delegate equality looks at each invocation in the invocation list, testing for equality of method to be invoked, and target of method.
The method is a simple piece of the cache key, but the target of the method (the instance to call it on - assuming an instance method) could be impossible to cache in a serializable way. In particular, for anonymous functions which capture state, it will be an instance of a nested class created to capture that state.
If this is all in memory, just keeping the delegate itself as the hash key will be okay - although it may mean that some objects which clients would expect to be garbage collected hang around. If you need to serialize this to a database, it gets hairier.
Could you make your method accept a cache key (e.g. a string) as well? (That's assuming an in memory cache is inadequate.)
Keeping the cached results in a Dictionary<Predicate<Foo>,List<Foo>> is awkward for me because I want the ASP.NET cache to handle expiry for me rather than caching all results forever, but it's otherwise a good solution. I think I'll end up going with Will's Dictionary<Predicate<Foo>,string> to cache a string that I can use in the ASP.NET cache key.
Some initial tests suggest that delegate equality does the "right thing" as others have said, but Delegate.GetHashCode is pathologically unhelpful. Reflector reveals
public override int GetHashCode()
{
return base.GetType().GetHashCode();
}
So any Predicate<Foo> returns the same result.
My remaining issue was how equality works for anonymous delegates. What does "same method called on the same target" mean then? It seems that as long as the delegate was defined in the same place, references are equal. Delegates with the same body defined in different places are not.
static Predicate<int> Test()
{
Predicate<int> test = delegate(int i) { return false; };
return test;
}
static void Main()
{
Predicate<int> test1 = Test();
Predicate<int> test2 = Test();
Console.WriteLine(test1.Equals( test2 )); // True
test1 = delegate(int i) { return false; };
test2 = delegate(int i) { return false; };
Console.WriteLine(test1.Equals( test2 )); // False
}
This should be OK for my needs. Calls with the predefined predicates will be cached. Multiple calls to one method that calls FindAll with an anonymous method should get cached results. Two methods calling FindAll with apparently the same anonymous method won't share cached results, but this should be fairly rare.
Unless you're sure Delegate's implementation of GetHashCode is deterministic and doesn't result in any collisions I wouldn't trust it.
Here's two ideas. First, store the results of the delegates within a Predicate/List dictionary, using the predicate as the key, and then store the entire dictionary of results under a single key in the cache. Bad thing is that you lose all your cached results if the cache item is lost.
An alternative would be to create an extension method for Predicate, GetKey(), that uses an object/string dictionary to store and retrieve all keys for all Predicates. You index into the dictionary with the delegate and return its key, creating one if you don't find it. This way you're assured that you are getting the correct key per delegate and there aren't any collisions. A naiive one would be type name + Guid.
The same instance of an object will always return the same hashcode (requirement of GetHashCode() in .Net). If your predicates are inside a static list and you are not redefining them each time, I can't see a problem in using them as keys.

Categories