Best way to compare two generic values? - c#

I'm writing a small class which I'm gonna move around when needed like a dll, and it's gonna have different sorting algorithms in it. I want the functions to work with any lists, of any types, including objects. So it's basically like this:
class TemplateSortings<T>
{
List<T> GNRList;
static void SortBubble<T>()
{
//Do stuff with GNRList, which can be a list of any values (nums, strings, objects)
}
}
Now the question I'm having troubles with is this - what is the best way to compare two generic values: overloading comparison operators or having the class inherit IComparable interface? What is better and why?

If you want it to work with any type, you probably shouldn't constrain T to types that implement IComparable, because not all types do.
A simple way to work around this is to let the caller decide how to compare the objects. You just need an extra parameter:
static void SortBubble(Func<T, T, int> comparator)
{
...
}
You can call comparator with 2 arguments and it will give you a negative value, 0, or a positive value indicating that the first parameter is less than, equal to, or greater than the second parameter.
As an example, you can call SortBubble with ints like this:
var sorting = new TemplateSortings<int>();
// populate the list...
sorting.SortBubble((x, y) => x.CompareTo(y)) // pass a lambda
EDIT:
If you don't want an extra parameter and want to check the type inside the method, you can do something like this:
if (typeof(IComparable<T>).IsAssignableFrom(typeof(T))) {
// do your sorting
// you need to cast values of type "T" to "Comparable<T>" like this
// var castedValue = (IComparable<T>)tValue;
} else {
throw ...
}

You could generalize your code so it can work with any potentially valid type T:
public static IEnumerable<T> BubbleSort(
this IEnumerable<T> source,
IComparer<T> comparer == null)
{
var currentComparer = comparer ?? Comparer<T>.Default;
//bubble sort with currentComparator
}
Now, you can sort any T whatsoever, if:
T implements IComparable<T>
T implements legacy IComparable
You hand down a Comparator that knows how to compare Ts
It will fail on any other scenario the moment you attempt to perform the first comparison.

Related

Cast from IEnumerable to IEnumerable<object>

I prefer to use IEnumerable<object>, for LINQ extension methods are defined on it, not IEnumerable, so that I can use, for example, range.Skip(2). However, I also prefer to use IEnumerable, for T[] is implicitly convertible to IEnumerable whether T is a reference type or value type. For the latter case, no boxing is involved, which is good. As a result, I can do IEnumerable range = new[] { 1, 2, 3 }. It seems impossible to combine the best of both worlds. Anyway, I chose to settle down to IEnumerable and do some kind of cast when I need to apply LINQ methods.
From this SO thread, I come to know that range.Cast<object>() is able to do the job. But it incurs performance overhead which is unnecessary in my opinion. I tried to perform a direct compile-time cast like (IEnumerable<object>)range. According to my tests, it works for reference element type but not for value type. Any ideas?
FYI, the question stems from this GitHub issue. And the test code I used is as follows:
static void Main(string[] args)
{
// IEnumerable range = new[] { 1, 2, 3 }; // won't work
IEnumerable range = new[] { "a", "b", "c" };
var range2 = (IEnumerable<object>)range;
foreach (var item in range2)
{
Console.WriteLine(item);
}
}
According to my tests, it works for reference element type but not for
value type.
Correct. This is because IEnumerable<out T> is co-variant, and co-variance/contra-variance is not supported for value types.
I come to know that range.Cast() is able to do the job. But it
incurs performance overhead which is unnecessary in my opinion.
IMO the performance cost(brought by boxing) is unavoidable if you want a collection of objects with a collection of value-types given. Using the non-generic IEnumerable won't avoid boxing because IEnumerable.GetEnumerator provides a IEnumerator whose .Current property returns an object. I'd prefer always use IEnumerable<T> instead of IEnumerable. So just use the .Cast method and forget the boxing.
After decompiling that extension, the source showed this:
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
if (enumerable != null)
return enumerable;
if (source == null)
throw Error.ArgumentNull("source");
return Enumerable.CastIterator<TResult>(source);
}
private static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source)
{
foreach (TResult result in source)
yield return result;
}
This basically does nothing else than IEnumerable<object> in first place.
You stated:
According to my tests, it works for reference element type but not for
value type.
How did you test that?
Despite I really do not like this approach, I know it is possible to provide a toolset similar to LINQ-to-Objects that is callable directly on an IEnumerable interface, without forcing a cast to IEnumerable<object> (bad: possible boxing!) and without casting to IEnumerable<TFoo> (even worse: we'd need to know and write TFoo!).
However, it is:
not free for runtime: it may be heavy, I didn't run perfomance test
not free for developer: you actually need to write all those LINQ-like extension methods for IEnumerable (or find a lib that does it)
not simple: you need to inspect the incoming type carefully and need to be careful with many possible options
is not an oracle: given a collection that implements IEnumerable but does not implement IEnumerable<T> it only can throw error or silently cast it to IEnumerable<object>
will not always work: given a collection that implements both IEnumerable<int> and IEnumerable<string> it simply cannot know what to do; even giving up and casting to IEnumerable<object> doesn't sound right here
Here's an example for .Net4+:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
public static void Main()
{
Console.WriteLine("List<int>");
new List<int> { 1, 2, 3 }
.DoSomething()
.DoSomething();
Console.WriteLine("List<string>");
new List<string> { "a", "b", "c" }
.DoSomething()
.DoSomething();
Console.WriteLine("int[]");
new int[] { 1, 2, 3 }
.DoSomething()
.DoSomething();
Console.WriteLine("string[]");
new string[] { "a", "b", "c" }
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with ints");
var stack = new System.Collections.Stack();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with mixed items");
new System.Collections.ArrayList { 1, "a", null }
.DoSomething()
.DoSomething();
Console.WriteLine("nongeneric collection with .. bits");
new System.Collections.BitArray(0x6D)
.DoSomething()
.DoSomething();
}
}
public static class MyGenericUtils
{
public static System.Collections.IEnumerable DoSomething(this System.Collections.IEnumerable items)
{
// check the REAL type of incoming collection
// if it implements IEnumerable<T>, we're lucky!
// we can unwrap it
// ...usually. How to unwrap it if it implements it multiple times?!
var ietype = items.GetType().FindInterfaces((t, args) =>
t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>),
null).SingleOrDefault();
if (ietype != null)
{
return
doSomething_X(
doSomething_X((dynamic)items)
);
// .doSomething_X() - and since the compile-time type is 'dynamic' I cannot chain
// .doSomething_X() - it in normal way (despite the fact it would actually compile well)
// `dynamic` doesn't resolve extension methods!
// it would look for doSomething_X inside the returned object
// ..but that's just INTERNAL implementation. For the user
// on the outside it's chainable
}
else
// uh-oh. no what? it can be array, it can be a non-generic collection
// like System.Collections.Hashtable .. but..
// from the type-definition point of view it means it holds any
// OBJECTs inside, even mixed types, and it exposes them as IEnumerable
// which returns them as OBJECTs, so..
return items.Cast<object>()
.doSomething_X()
.doSomething_X();
}
private static IEnumerable<T> doSomething_X<T>(this IEnumerable<T> valitems)
{
// do-whatever,let's just see it being called
Console.WriteLine("I got <{1}>: {0}", valitems.Count(), typeof(T));
return valitems;
}
}
Yes, that's silly. I chained them four (2outsidex2inside) times just to show that the type information is not lost in subsequent calls. The point was to show that the 'entry point' takes a nongeneric IEnumerable and that <T> is resolved wherever it can be. You can easily adapt the code to make it a normal LINQ-to-Objects .Count() method. Similarly, one can write all other operations, too.
This example uses dynamic to let the platform resolve the most-narrow T for IEnumerable, if possible (which we need to ensure first). Without dynamic (i.e. .Net2.0) we'd need to invoke the dosomething_X through reflection, or implement it twice as dosomething_refs<T>():where T:class+dosomething_vals<T>():where T:struct and do some magic to call it properly without actually casting (probably reflection, again).
Nevertheless, it seems that you can get something-like-linq working "directly" on things hidden behind nongeneric IEnumerable. All thanks to the fact that the objects hiding behind IEnumerable still have their own full type information (yeah, that assumption may fail with COM or Remoting). However.. I think settling for IEnumerable<T> is a better option. Let's leave plain old IEnumerable to special cases where there is really no other option.
..oh.. and I actually didn't investigate if the code above is correct, fast, safe, resource-conserving, lazy-evaluating, etc.
IEnumerable<T> is a generic interface. As long as you're only dealing with generics and types known at compile-time, there's no point in using IEnumerable<object> - either use IEnumerable<int> or IEnumerable<T>, depending entirely on whether you're writing a generic method, or one where the correct type is already known. Don't try to find an IEnumerable to fit them all - use the correct one in the first place - it's very rare for that not to be possible, and most of the time, it's simply a result of bad object design.
The reason IEnumerable<int> cannot be cast to IEnumerable<object> may be somewhat surprising, but it's actually very simple - value types aren't polymorphic, so they don't support co-variance. Do not be mistaken - IEnumerable<string> doesn't implement IEnumerable<object> - the only reason you can cast IEnumerable<string> to IEnumerable<object> is that IEnumerable<T> is co-variant.
It's just a funny case of "surprising, yet obvious". It's surprising, since int derives from object, right? And yet, it's obvious, because int doesn't really derive from object, even though it can be cast to an object through a process called boxing, which creates a "real object-derived int".

How to cast a generic type to a non-generic type

I have a method that looks like this (assume that I have the necessary method GetMySerializedDataArry() and my serializer JsonSerializer):
public static List<T> GetMyListOfData<T>()
{
var msgList = new List<T>();
foreach (string s in GetMySerializedDataArray())
{
msgList.Add(JsonSerializer.Deserialize<T>(s));
}
return msgList;
}
This works fine and as expected.
However, I want to use the same method to optionally, if and only if the generic type is specified as string, return the data unserialized like this (which does not compile and has syntax problems):
public static List<T> GetMyListOfData<T>(bool leaveSerialized)
{
if (typeof (T) != typeof(string) && leaveSerialized)
{
throw new ArgumentException("Parameter must be false when generic type is not List<string>", "leaveSerialized");
}
var msgList = new List<T>();
foreach (string s in GetMySerializedDataArray())
{
if (leaveSerialized)
{
// Casting does not work: "Cannot cast expression of type 'System.Collections.Generic.List<T>' to type 'List<string>'"
// I've tried various permutations of "is" and "as"... but they don't work with generic types
// But I know in this case that I DO have a list of strings..... just the compiler doesn't.
// How do I assure the compiler?
((List<string>)msgList).Add(s);
}
else
{
msgList.Add(JsonSerializer.Deserialize<T>(s));
}
}
return msgList;
}
My questions are in the inline comment.... basically though the compiler clearly doesn't like the cast of generic to non-generic, it won't let me use permutations of "is" and "are" operators either, I know I actually have the correct string in this case.... how to assure the compiler it is OK?
Many thanks in advance.
EDIT: SOLUTION
Thanks to Lee and Lorentz, both. I will be creating two public methods, but implementing the code in a private method with the admittedly icky decision tree about whether to leave serialization. My reason is that my real-world method is far more complex than what I posed here to SO, and I don't want to duplicate those business rules.
FINAL EDIT: CHANGED SOLUTION
Although both answers were very helpful, I have now been able to detangle business rules, and as a result the "correct" answer for me is now the first -- two different methods. Thanks again to all.
You should not return a list of strings as a list of T. I would suggest that you use two separate methods and skip the parameter:
public static List<T> GetMyListOfData<T>()
public static List<string> GetSerializedMyListOfData()
The advantages of this approach is
It's more readable (imo) GetSerializedMyListOfData() vs GetMyListOfData<string>(true)
You also know the intent of the caller at compile time and don't have to throw an exception when the type argument don't match the intent to leave the data serialized
You can cast to object first:
((List<string>)(object)msgList).Add(s);
however a cleaner solution could be to create another method for dealing with strings, this would also allow you to remove the leaveSerialized parameter.

Sorted Dictionary - C#

I'm trying to figure out how to create a sorted dictionary where the key is sorted in a non-alphabetical manner. Is there a way I can define the way I want it to sort?
For example, the keys might be in order like the following:
AAA1X
AAB1Y
AAC1Y
AAA2X
AAB2Y
AAC2X
Although the first three letters are alphabetical, if I sort as is it will lump them in the wrong order (due to the number). Also note, that there is either a X or Y at the end. In the code, there will only ever be an X or a Y.
Even if I can write an enumeration for the ordering of all possible combinations I'd be willing to do that as well, but I'm not sure how I can use the sorted dictionary and an enumeration...
I know this is a bit vague, but any help would be much appreciated!
Cheers!
One of the constructors for SortedDictionary<TKey, TValue> takes an IComparer<TKey>, where you can specify a custom comparer class that the dictionary will use for sorting.
public class CustomComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// do your own comparison however you like; return a negative value
// to indicate that x < y, a positive value to indicate that x > y,
// or 0 to indicate that they are equal.
}
}
...
SortedDictionary<string, object> dict =
new SortedDictionary<string, object>(new CustomComparer());
You can :
Create a class that encapsulates those keys and override the == operator, Object.Equals method and the Object.GetHashCode method. Also have the class implement the IComparable interface such that the rules for equality will automatically sort your keys the right way. Now you can simply place those keys in a SortedDictionary or SortedList and it will automatically sort the way you want.
OR
Implement an IComparer object that compares those strings the way you want, and specify the IComparer when constructing a SortedDictionaryor SortedList.

Using IComparer<> with delegate function to search

This feels like a too easy question to be found with google, I think/hope I've got stuck in the details when trying to implement my own version of it. What I'm trying to do is to sort a list of MyClass objects depending on my Datatype object different search functions should be used.
I've had something like this in mind for the class Datatype:
class Datatype {
public delegate int CMPFN(object x, object y);
private CMPFN compareFunction;
(...)
private XsdDatatype((...), CMPFN compareFunction) {
(...)
this.compareFunction = compareFunction;
}
public CMPFN GetCompareFunction() {
return this.compareFunction;
}
static private int SortStrings(object a, object b) {
return ((MyClass)a).GetValue().CompareTo(((MyClass)b).GetValue());
}
}
And later on I'm trying to sort a MyClass list something like this:
List<MyClass> elements = GetElements();
Datatype datatype = new Datatype((...), Datatype.SortStrings);
elements.Sort(datatype.GetCompareFunction()); // <-- Compile error!
I'm not overly excited about the cast in Datatype.SortStrings but it feels like this could work(?). The compiler however disagrees and gets me this error on the last line above and I'm a bit unsure exactly why CMPFN can't be converted/casted(?) to IComparer.
Cannot convert type 'proj.Datatype.CMPFN' to 'System.Collections.Generic.IComparer<proj.MyClass>'
Delegates aren't duck-typed like that. You can create an Comparison<MyClass> from a CMPFN but you can't use a plain reference conversion - either implicit or explicit.
Three options:
Create the comparer like this:
elements.Sort(new Comparison<MyClass>(datatype.GetCompareFunction()));
Use a lambda expression to create a Comparison<T> and use that instead:
elements.Sort((x, y) => datatype.GetCompareFunction()(x, y));
Write an implementation of IComparer<MyClass> which performs the comparison based on a CMPFN
Note that the second approach will call GetCompareFunction once per comparison.
A much better solution would be to get rid of CMPFN entirely - why not just use (or implement) IComparer<MyClass> to start with? Note that that would remove the casts as well. (If you're happy using delegates instead of interfaces, you could express the comparison as a Comparison<MyClass> instead.)
Note that as of .NET 4.5, you can use Comparer.Create to create a Comparer<T> from a Comparison<T> delegate.
I'm not sure why your current API is in terms of object, but you should be aware that in C# 3 and earlier (or C# 4 targeting .NET 3.5 and earlier) you wouldn't be able to convert an IComparer<object> into an IComparer<MyClass> (via a reference conversion, anyway). As of C# 4 you can, due to generic contravariance.
There are a number of overloads of List<T>.Sort, but there are none which take a delegate with the parameters you have defined (two objects).
However, there is an overload that takes a Comparison<T> delegate, which you can work with your code with a few minor modifications. Basically, you just replace your CMPFN delegate with Comparison<MyClass> - as an added bonus, you get strong-typing in your SortStrings function, too:
static private int SortStrings(MyClass a, MyClass b) {
return a.GetValue().CompareTo(b.GetValue());
}
public Comparison<MyClass> GetCompareFunction() {
return SortStrings; // or whatever
}
...
elements.Sort(datatype.GetCompareFunction());
Try something like this
class AttributeSort : IComparer<AttributeClass >
{
#region IComparer Members
public int Compare(AttributeClass x, AttributeClass y)
{
if (x == null || y == null)
throw new ArgumentException("At least one argument is null");
if (x.attributeNo == y.attributeNo) return 0;
if (x.attributeNo < y.attributeNo) return -1;
return 1;
}
#endregion
}
You can call it then like this
List<AttributeClass> listWithObj ....
listWithObj.Sort(new AttributeSort());
Should work like you want. You can create a type-safe comparer class as well.

Problem sorting lists using delegates

I am trying to sort a list using delegates but I am getting a signature match error. The compiler says I cannot convert from an 'anonymous method'
List<MyType> myList = GetMyList();
myList.Sort( delegate (MyType t1, MyType t2) { return (t1.ID < t2.ID); } );
What am I missing?
Here are some references I found and they do it the same way.
Developer Fusion Reference
Microsoft Reference
I think you want:
myList.Sort( delegate (MyType t1, MyType t2)
{ return (t1.ID.CompareTo(t2.ID)); }
);
To sort you need something other than "true/false", you need to know if its equal to, greater than, or less than.
The Sort doesn't take a binary predicate, it takes a Comparison<T> delegate which returns an int not a bool.
The return values are 0 for when the items are equal, <0 for when the first item is less than the second, and >0 for when the first item is greater than the second.
In future, if you want to debug problems like this, I'd advocate breaking out the delegate definition from the Sort call, like this:
Comparison<MyType> c = delegate(MyType t1, MyType t2){ ... };
myList.Sort(c);
That way, you can see if the problem is in your method call, or in your delegate definition. Some people prefer to leave it this way (with a more descriptive name than "c", obviously) to make the code more readable. I could take it or leave it =-)
The way of obj.Sort(delegate(...)); is dynamic sorting in one place. If you have several places doing the same sorting or you need more flexible sorting, you may consider to create a class implementing IComparer<T>. Here is an example:
public class MyTypeComparer : IComparer<MyType>
{
public MyTypeComparer() // default comparer on ID
{ ... }
public MyTypeComparer(bool desc) // default with order specified
public MyTypeComparer(string sort, bool desc) // specified sort and order such as property name, true or false.
{ ... }
public int Compare(MyType a, MyType b) // implement IComparer interface
{ ... } // this is real sorting codes
}
and here is the example to use it:
List<MyType> myList = GetList();
myList.Sort(new MyTypeComparer());
// myList.Sort(new MyTypeComparer(false));
// myList.Sort(new MyTypeComparer("FirstName", true));
Make sure if your ID property is the default value data type, such as Int or String. If the ID is an object reference type, that object should implement IComparer or IComparer.
Sorry for previous post. The editor does not take < and > characters, and I did not notice the preview right under the editor. If the ID property is an object type, the object should implement IComparer or IComparer<T>.

Categories

Resources