Implement CompareTo() - compare by various functions - c#

I implemented CompareTo() like so:
public override int CompareTo(object obj)
{
//quick failsafe
MyClass other;
if (obj is MyClass)
{
other = obj as MyClass;
}
else
{
return 1;
}
//now we should have another comparable object.
/*
* 1: this is greater.
* 0: equals.
* -1: this is less.
*/
if (other.GetValue() < this.GetValue())
{
// this is bigger
return 1;
}
else if (other.GetValue() > this.GetValue())
{
//this is smaller
return -1;
}
else
{
return 0;
}
}
However, things get interesting when I want to chose the function GetValue(). I have a couple of them set up for that: namely Average(), Best(), CorrectedAverage(), Median(). I compare by an array of floats by the way. Thing is, I don't want to use a switch-case on an enum I defined in this class to tell what to order by. Is there a way that I decide which function to order by nice and clean?

Given that your class has a whole bunch of different ways of comparing it, it almost certainly shouldn't implement IComparable at all.
Instead, create IComparer<T> instances for each different way of comparing your object. Someone who wants to comparer instances of the type can then pick the comparer that uses the comparison that's most appropriate for their situation.

Related

Can IEnumerable.Count() return a negative number? [duplicate]

This question already has answers here:
Why does .NET use int instead of uint in certain classes?
(7 answers)
Closed 4 years ago.
The C# IEnumerable.Count(IEnumerable<TSource>) and IEnumerable.Count(IEnumerable<TSource>, Func<TSource,Boolean>) both return type int implying it can return a value less than zero. It doesn't make sense for it to do so, but if the type is int it's theoretically possible for the result to be a negative value.
IEnumerable<string> list = GetMyList();
int listCount = list.Count();
// is it more correct to do this:
if(listCount <= 0)
{
DoSomething();
}
else
{
DoSomethingElse();
}
// or this:
if(listCount == 0)
{
DoSomething();
}
else if (listCount > 0)
{
DoSomethingElse();
}
else
{
// but this branch will never be hit
throw new Exception();
}
I can't find any information online about whether or not that can actually happen, and the Documentation for Enumerable.Count does not specify any cases in which it might.
Just wondering if anyone has any experience with this happening or any information on this.
Thanks
The purpose of the return data type is not to imply a range of numbers. Although it naturally does set a hard upper and lower limit (sort of... see LongCount), that is just a side effect of type compatibility and generalizability.
Consider the Array's Rank property. The maximum value is 32. But we don't store it in a byte or short. We store it in an int. Why? We don't need all that range. But it's recommended: they're fast (they align well to register size and memory maps) and, by convention, It is easier to work with other libraries if you work with ints. Also, the int datatype is CLS-compliant (meaning that any language that implements the CLR must support it) but uint32 is not.
Returning a numeric data type that has a particular range in no way implies that the full range will be used. And returning a negative value from IEnumerable.Count() would not only be poor form, but it would be semantically incorrect, as a count must obviously return a cardinal number, which must be and integer and non-negative.
Actually there´s no such method Count defined on neither IEnumerable nor IEnumerable<T>, but on the static class System.Linq.Enumerable. As it´s a (static) extension-method, you can´t override nor modify this at all. So let´s look into the extension-method:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
checked {
while (e.MoveNext()) count++;
}
}
return count;
}
As you can see the only way Count will ever return a negative number is by implelementing ICollection.Count, which is called by Enumerable.Count() (as you can see above), or by creating your own extension-method with the exact same name and relying on extension-method resolution in order to "hide" the method from System.Linq.Enumerable:
public static class MyClass
{
public static int Count(this IEnumerable<T> src) { return -1; }
}
However I can´t see any reason why one should do this at all.
So in short the method can return a negative number. Doing this however breaks the principle of least astonishment and thus is a bad idea.

Elegant way to query a dictionary in C#

I am trying to create an elegant and extensible way of querying a dictionary which maps an enum to a set of strings.
So I have this class SearchFragments that has the dictionary in it. I then want a method wherein consumers of this class can simply ask "HasAny" and, this is the bit where I am struggling, simply pass in some query like expression and get the boolean answer back.
public class SearchFragments
{
private readonly IDictionary<SearchFragmentEnum, IEnumerable<string>> _fragments;
public SearchFragments()
{
_fragments = new Dictionary<SearchFragmentEnum, IEnumerable<string>>();
}
public bool HasAny(IEnumerable<SearchFragmentEnum> of)
{
int has = 0;
_fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));
return has >= 1;
}
}
The problem with the way this currently is, is that consumers of this class now have to construct an IEnumerable<SearchFragmentEnum> which can be quite messy.
What I am looking for is that the consuming code will be able to write something along the lines of:
searchFragments.HasAny(SearchFragmentEnum.Name, SearchFragmentEnum.PhoneNumber)
But where that argument list can vary in size (without me having to write method overloads in the SearchFragments class for every possible combination (such that if new values are added to the SearchFragmentEnum at a future date I won't have to update the class.
You can use params[]
public bool HasAny(params SearchFragmentEnum[] of)
{ ...
Sidenote: you know that LIN(Q) queries should just query a source and never cause any side-effects? But your query does unnecessarily increment the integer:
_fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));
Instead use this (which is also more efficient and more readable):
return _fragments.Keys.Intersect(of).Any();
An even more efficient alternative to this is Sergey's idea:
return of?.Any(_fragments.ContainsKey) == true;
For variable sized arguments in c# you use the params keyword:
public int HasAny(params SearchFragmentEnum[] of)
The .Net API usually offers a couple of overloads of this for performance reasons; the parameters passed are copied into a new array. Explicitely providing overloads for the most common cases avoids this.
public int HasAny(SearchfragmentEnum of1)
public int HasAny(SearchFragmentEnum of1, SearchFragmentEnum of2)
etc.
Instead of using params you could also consider marking your enum with the [Flags] attribute. Parameters could than be passed like HasAny(SearchFragmentEnum.Name | SearchFragmentEnum.PhoneNumber. Examples abundant on StackOverflow (e.g. Using a bitmask in C#)
Use the params keyword to allow a varying number of arguments. Further, you can simplify your code by looping over the smaller of array. Also, you are using a dictionary that has O(1) key check, so it is uneccessary to have an inner loop:
public bool HasAny(params SearchFragmentEnum[] of)
{
foreach(var o in of) {
if (this._fragments.ContainsKey(o))
return true;
}
return false;
}
or shorter with LINQ
public bool HasAny(params SearchFragmentEnum[] of) {
return of?.Any(_fragments.ContainsKey) ?? false;
}

Using Linq Except not Working as I Thought

List1 contains items { A, B } and List2 contains items { A, B, C }.
What I need is to be returned { C } when I use Except Linq extension. Instead I get returned { A, B } and if I flip the lists around in my expression the result is { A, B, C }.
Am I misunderstanding the point of Except? Is there another extension I am not seeing to use?
I have looked through and tried a number of different posts on this matter with no success thus far.
var except = List1.Except(List2); //This is the line I have thus far
EDIT: Yes I was comparing simple objects. I have never used IEqualityComparer, it was interesting to learn about.
Thanks all for the help. The problem was not implementing the comparer. The linked blog post and example below where helpful.
If you are storing reference types in your list, you have to make sure there is a way to compare the objects for equality. Otherwise they will be checked by comparing if they refer to same address.
You can implement IEqualityComparer<T> and send it as a parameter to Except() function. Here's a blog post you may find helpful.
edit: the original blog post link was broken and has been replaced above
So just for completeness...
// Except gives you the items in the first set but not the second
var InList1ButNotList2 = List1.Except(List2);
var InList2ButNotList1 = List2.Except(List1);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
Edit: Since your lists contain objects you need to pass in an IEqualityComparer for your class... Here is what your except will look like with a sample IEqualityComparer based on made up objects... :)
// Except gives you the items in the first set but not the second
var equalityComparer = new MyClassEqualityComparer();
var InList1ButNotList2 = List1.Except(List2, equalityComparer);
var InList2ButNotList1 = List2.Except(List1, equalityComparer);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
public class MyClass
{
public int i;
public int j;
}
class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.i == y.i &&
x.j == y.j;
}
public int GetHashCode(MyClass obj)
{
unchecked
{
if (obj == null)
return 0;
int hashCode = obj.i.GetHashCode();
hashCode = (hashCode * 397) ^ obj.i.GetHashCode();
return hashCode;
}
}
}
You simply confused the order of arguments. I can see where this confusion arose, because the official documentation isn't as helpful as it could be:
Produces the set difference of two sequences by using the default equality comparer to compare values.
Unless you're versed in set theory, it may not be clear what a set difference actually is—it's not simply what's different between the sets. In reality, Except returns the list of elements in the first set that are not in the second set.
Try this:
var except = List2.Except(List1); // { C }
Writing a custom comparer does seem to solve the problem, but I think https://stackoverflow.com/a/12988312/10042740 is a much more simple and elegant solution.
It overwrites the GetHashCode() and Equals() methods in your object defining class, then the default comparer does its magic without extra code cluttering up the place.
Just for Ref:
I wanted to compare USB Drives connected and available to the system.
So this is the class which implements interface IEqualityComparer
public class DriveInfoEqualityComparer : IEqualityComparer<DriveInfo>
{
public bool Equals(DriveInfo x, DriveInfo y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
// compare with Drive Level
return x.VolumeLabel.Equals(y.VolumeLabel);
}
public int GetHashCode(DriveInfo obj)
{
return obj.VolumeLabel.GetHashCode();
}
}
and you can use it like this
var newDeviceLst = DriveInfo.GetDrives()
.ToList()
.Except(inMemoryDrives, new DriveInfoEqualityComparer())
.ToList();

Is there a class in C# to handle a couple of INT (range of 2 INT- 1-10)

I am quite new to C# and I was wondering if there is a Class or a data structure or the best way to handle the following requirement...
I need to handle a COUPLE of int that represent a range of data (eg. 1 - 10 or 5-245) and I need a method to verify if an Int value is contained in the range...
I believe that in C# there is a class built in the framework to handle my requirement...
what I need to do is to verify if an INT (eg. 5) is contained in the range of values Eg (1-10) ...
in the case that I should discover that there is not a class to handle it, I was thinking to go with a Struct that contain the 2 numbers and make my own Contain method to test if 5 is contained in the range 1-10)
in the case that I should discover that there is not a class to handle
it, I was thinking to go with a Struct that contain the 2 numbers and
make my own Contain method to test if 5 is contained in the range
1-10)
That's actually a great idea as there's no built-in class for your scenario in the BCL.
You're looking for a range type; the .Net framework does not include one.
You should make an immutable (!) Int32Range struct, as you suggested.
You may want to implement IEnumerable<int> to allow users to easily loop through the numbers in the range.
You need to decide whether each bound should be inclusive or exclusive.
[Start, End) is probably the most obvious choice.
Whatever you choose, you should document it clearly in the XML comments.
Nothing exists that meets your requirements exactly.
Assuming I understood you correctly, the class is pretty simple to write.
class Range
{
public int Low {get; set;}
public int High {get; set;}
public bool InRange(int val) { return val >= Low && val <= High; }
}
A Tuple<int,int> would get you part of the way but you'd have to add an extension method to get the extra behavior. The downside is that the lower- and upper-bounds are implicitly Item1 and Item2 which could be confusing.
// written off-the-cuff, may not compile
public static class TupleExtension
{
public static bool InRange(Tuple<int, int> this, int queryFor)
{
return this.Item1 >= queryFor && this.Item2 <= queryFor;
}
}
You could create an extension if you want to avoid making a new type:
public static class Extensions
{
public static bool IsInRange(this int value, int min, int max)
{
return value >= min && value <= max;
}
}
Then you could do something like:
if(!value.IsInRange(5, 545))
throw new Exception("Value is out of range.");
i think you can do that with an array.
some nice examples and explanation can be found here:
http://www.dotnetperls.com/int-array
Nothing built in AFAIK, but (depending on the size of the range) an Enumerable.Range would work (but be less than optimal, as you're really storing every value in the range, not just the endpoints). It does allow you to use the LINQ methods (including Enumerable.Contains), though - which may come in handy.
const int START = 5;
const int END = 245;
var r = Enumerable.Range(START, (END - START)); // 2nd param is # of integers
return r.Contains(100);
Personally, I'd probably go ahead and write the class, since it's fairly simple (and you can always expose an IEnumerable<int> iterator via Enumerable.Range if you want to do LINQ over it)

C# Subclass with same method

I have a superclass with two subclasses. The two subclasses both have a method with checks whether a chapter has content. For subclass 1 this method is HasContent(int chapterID) and for subclass 2 this is HasContent(int chapterID, int institution). As you can see subclass 2 has an extra parameter. The purpose of both methods is the same.
I was thinking to put the method HasContent in the superclass. Do you think i need to do this? If so, how should i implement this? Or is it more wisely to put both methods in their own subclass?
EDIT:
The body of HasDocuments looks like this:
Subclass1:
Database DB = new Database();
int res = DB.ExecuteSpRetVal(chapterID, mInstitutionID);
if (res > 0)
return true;
else
return false;
Subclass2:
Database DB = new Database();
int res = DB.ExecuteSpRetVal(chapterID);
if (res > 0)
return true;
else
return false;
Edit: Updated according to the question update.
Since you are clearly having almost the same logic in both methods, I'd refactor it like this:
abstract class SuperClass
{
protected bool HasContentImpl(int chapterID, int institution)
{
Database db = new Database();
int result;
if (institution >= 0) // assuming negative numbers are out of range
result = db.ExecuteSpRetVal(chapterID, institution);
else
result = db.ExecuteSpRetVal(chapterID);
return result > 0;
}
}
class SubClass1 : SuperClass
{
public bool HasContent(int chapterID)
{
return base.HasContentImpl(chapterID, -1);
}
}
class SubClass2 : SuperClass
{
public bool HasContent(int chapterID, int institution)
{
return base.HasContentImpl(chapterID, institution);
}
}
Use method overloading by placing two identically named methods with different arguments into the superclass. When you call HasContent, it will use whichever one matches the number and types of arguments you have provided. Because it is in the superclass, you now won't have to make yet another copy of it if you decide later to make a new subclass that uses this method as well. See below for example code;
protected bool HasContent(int chapterID, int institution)
{
Database db = new Database();
int result;
result = db.ExecuteSpRetVal(chapterID, institution);
return result > 0;
}
protected bool HasContent(int chapterID)
{
Database db = new Database();
int result;
result = db.ExecuteSpRetVal(chapterID);
return result > 0;
}
You said:
The purpose of both methods is the same
so yes, it does sounds that you've got a common method that you can put in the superclass.
As HasContent() takes different augments I would not move it up to the base class, the fact that two methods are called the same, does not mean they do the same thing.
I don’t know your code base or what the system you are working on does, however given the little information I have, some thing about your design feels wronge. I have found in the past that often when I have this sort of design problem, it is due to a problem else where in how the data is model. Sorry not match help if you can’t change the rest of the system….

Categories

Resources