debracey
debracey

Reputation: 6607

Get Default Equality Comparer from Variable Type?

Is there a way to obtain the default comparer for a given type, where the type is variable and known only at runtime? Consider the following:

var RT = typeof(string);
var comparer = EqualityComparer<RT>.Default;

This obviously does not compile, but if it did, comparer should have a value equal to that of EqualityComparer<string>.Default

The only way that I can think of to achieve this is to make a "boxed" comparer that you can invoke via reflection (see below). This works, but it is quite cumbersome. Is there any better way to do this?

To Clarify, This is not a good idea because of reflection, but why do I need it?

The algorithm in question is part of a large legacy search API. The consumer passes a list of objects (e.g., List<Person>) to the API internally creates type specific indexes (using reflection) so that the caller can then search on any field within that object (e.g., perhaps the last name). This would normally not be necessary, but in the use case I am servicing we're searching very large sets against other very large sets. A database stored procedure is probably better for this purpose. But for now I need to patch this legacy API to support a user defined comparison algorithm, and also support for cases when the user chooses not to supply any comparison algorithm, where I only know the runtime type RT.

// Example usage
// Assume "RT" is a Type known only at runtime (e.g., typeof(string))
var box = typeof(BoxedComparer<>);

var generic = box.MakeGenericType(RT);
var specific = (IBoxedComparer) Activator.CreateInstance(generic);

// Now with specific you can get the equality comparer for the runtime type (RT)
var comparer = specific.GetEqualityComparer();

public interface IBoxedComparer
{
   // You need the interface to allow a "typeless" cast
   EqualityComparer GetEqualityComparer()
}

public BoxedComparer<T> : IBoxedComparer 
{
   public EqualityComparer GetEqualityComparer() { return EqualityComparer<T>.Default; }
}

Upvotes: 0

Views: 1241

Answers (1)

Sean Skelly
Sean Skelly

Reputation: 1344

To future web-searchers:

Reflection is not the recommended way to do this. Probably refactoring is in order. For example, it's not clear how var comparer would actually be used once it is created via Reflection in this way.

To mitigate performance concerns, you could cache the comparers as they are created, much like the source of EqualityComparer<T> does internally for each T. But your code just gets messier this way.

That said...

A simpler, one-line way to accomplish the Reflection:

var comparer = typeof(EqualityComparer<>).MakeGenericType(RT).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null);

where RT is the runtime Type instance that we don't know at compile time.

Requires:

using System.Collections.Generic;
using System.Reflection;

Note that when accessing a static field or property via reflection, it is expected to pass null to GetValue/SetValue (where normally you pass the object containing the property).

Again, this code isn't the recommended approach. Readability, reusability, maintenance, and error handling options aren't really considered. This example just shows a simpler way to get what's needed without introducing extra classes.

Upvotes: 3

Related Questions