IComparer C# + generics - c#

How I can write make method Compare in RoomComparerByVolume ?
"Define a generic class RoomComparerByVolume<> implementing IComparer interface.
Impose a constraint on the type argument so that it should implement the IShape interface.
This comparer should perform comparison of rooms by room volume."
public interface IShape
{
public double Area()
{
return 0;
}
}
public class Rectangle : IShape
{
public double Length { get; set; }
public double Width { get; set; }
public double Area()
{
return Length * Width;
}
}
public class Trapezoid : IShape
{
public double Length1 { get; set; }
public double Length2 { get; set; }
public double Width { get; set; }
public double Area()
{
return (Length1 + Length2) * Width / 2;
}
}
public class Room<T> where T : IShape, ICloneable, IComparable
{
public double Height { get; set; }
public T Floor;
public double Volume()
{
return Height * Height;
}
public object Clone()
{
return new Room<T> { Height = this.Height, Floor = this.Floor };
}
public int CompareTo(object o)
{
Room<T> r = o as Room<T>;
if (r != null)
return this.Volume().CompareTo(r.Volume());
else
throw new Exception("Unable to compare");
}
}
public class RoomComparerByVolume<T> : IComparer<T> where T : IShape
{
}

Your question is a bit unclear. If compare "by room volume" means in fact compare Area() values
and you want to imeplement a corresponding comparer, you can put something like this
public sealed class ShapeComparerByArea<T> : IComparer<T> where T : IShape
{
public int Compare(T left, T right)
{
if (ReferenceEquals(left, right))
return 0;
if (left == null)
return 1;
if (right == null)
return -1;
return left.Area().CompareTo(right.Area());
}
}
If you want to define comparable Room class (which has Shape Floor) you can put it as
public class Room : IComparable<Room> {
public Room(IShape floor, double height) {
Floor = floor ?? throw new ArgumentNullException(nameof(floor));
Height = height > 0
? height
: throw new ArgumentOutOfRangeException(nameof(height));
}
public IShape Floor { get; }
public double Height { get; }
public double Volume => Floor.Area() * Height;
public int CompareTo(Room other) {
if (ReferenceEquals(this, other))
return 0;
if (other is null)
return 1;
return Volume.CompareTo(other.Volume);
}
}
Finally, RoomComparerByVolume can be
public sealed class RoomComparerByVolume : IComparer<Room> {
public int Compare(Room left, Room right) {
if (ReferenceEquals(left, right))
return 0;
if (left == null)
return 1;
if (right == null)
return -1;
return left.Volume.CompareTo(right.Volume);
}
}
Demo:
Room myRoom = new Room(
new Trapezoid() { Length1 = 5, Length2 = 7, Width = 3},
2.8);
Room myOtherRoom = new Room(
new Rectangle() { Length = 3, Width = 4 },
2.5);
Console.WriteLine(myRoom.CompareTo(myOtherRoom));
RoomComparerByVolume comparer = new RoomComparerByVolume();
Console.WriteLine(comparer.Compare(myRoom, myOtherRoom));

I would assume that the desired declaration should look like
public class RoomComparerByVolume<T> : IComparer<Room<T>> where T : IShape, ICloneable, IComparable{
public int Compare(Room<T> left, Room<T> right) {
...
}
That way you can compare the two rooms. But note that the two rooms need to be the same shape using this method. Also note that you need to use the same generic constraints as you are using for Room<T>.
Also note that in many cases you do not need to create a specific comparer type. Often you can use a delegate to do the actual comparison:
Comparer<Room<Trapezoid>>.Create((l, r) => l.Volume().CompareTo(r.Volume));

Related

How do you compare objects with different properties in run-time?

My code structure is something like this
class Drawing()
{
ObjectType = "Drawing";
}
class Shape() : Drawing()
{
DrawingType = "Shape";
}
class square() : Shape()
{
ShapeType = "square";
}
class circle() : Shape()
{
ShapeType = "circle";
}
class triangle() : Shape()
{
ShapeType = "triangle";
}
class lines() : Drawing()
{
DrawingType = "lines";
}
class horizontal() : lines()
{
linesType = "horizontal";
}
class vertical() : lines()
{
linesType = "vertical";
}
etc...
I am trying to figure out the right logic in how to solve this problem so the picture is a simple representation of what I have.
I have a object structure of something like in the picture where they're all inheriting the level above them. They all have properties of its type. They would all have ObjectType, and the 2nd level has DrawingType, and the 3rd level has either ShapeType, LineType, or PointType...
So for example, square would have
square {
ObjectType = "drawing";
DrawingType = "shapes";
ShapeType = "square"
}
However, vertical would have different properties
vertical {
ObjectType = "drawing";
DrawingType = "lines";
LineType = "vertical"
}
Now my problem is, lets say I wanted a user to select something based on their input, how would i do it?
Like lets say the user typed "square", how would I select square? It would be easy if the properties were the same cause I could just compare them. But how would I do it with a structure like this?
As these all classes are derived from one base class hence all the child classes contains the property coming from parents. Object level comparison will lead you comparison of one Drawing object to another as all the objects are derived from there only. Hence i have implemented IComparer to get your desired result ->
Here is the code ->>
Main Class :
Drawing drawing1 = new Drawing();
Drawing drawing2 = new Drawing();
drawing2.ObjectType = "newdrawing";
Shape shape = new Shape();
triangle triangle1 = new triangle();
triangle triangle2 = new triangle();
triangle2.ShapeType = "another";
//all -1 is not equals and 0 is equals
int result1 = drawing1.Compare(drawing1, drawing2);
int result2 = drawing1.Compare(drawing1, drawing1);
int result3 = drawing1.Compare(drawing1, shape);
int result4 = shape.Compare(shape, triangle1);
int result5 = triangle1.Compare(triangle1, triangle2);
Other classes :
public class Drawing : IComparer
{
public string ObjectType { get; set; } = "Drawing";
public int Compare(object x, object y)
{
//returning 0 is equals
if (x.GetType().Name == y.GetType().Name)
{
switch (x.GetType().Name)
{
case "Drawing":
return ((Drawing)x).ObjectType == ((Drawing)y).ObjectType ? 0 : -1;
case "Shape":
return ((Shape)x).DrawingType == ((Shape)y).DrawingType ? 0 : -1;
case "square":
return ((square)x).ShapeType == ((square)y).ShapeType ? 0 : -1;
case "triangle":
return ((triangle)x).ShapeType == ((triangle)y).ShapeType ? 0 : -1;
default:
return -1; // Not equal
}
}
return -1; // Not equal
}
}
class Shape : Drawing
{
public string DrawingType { get; set; } = "Shape";
}
class square : Shape
{
public string ShapeType { get; set; } = "square";
}
class circle : Shape
{
public string ShapeType { get; set; } = "circle";
}
class triangle : Shape
{
public string ShapeType { get; set; } = "triangle";
}
class lines : Drawing
{
public string DrawingType { get; set; } = "lines";
}
class horizontal : lines
{
public string linesType { get; set; } = "horizontal";
}
class vertical : lines
{
public string linesType { get; set; } = "vertical";
}
Hope this will work for you. please write back with you code snippet specifically not working. Cheers

Problem with Interface Implementation When using Inheritance

I have created IShallowCloneable interface to create shallow copy of class for All of my class but with inheritance it is not working properly.
Look at Main method, node2 is returning Point3D object Instead of Node.
Details
Point2D is Base Class
Point3D Class Derived from Point2D Class
Node
Class Derived from Point3D Class
using System;
namespace ConsoleAppTest
{
internal static class Program
{
private static void Main()
{
try
{
var node = new Node(1, 0, 0, 0);
var node2 = node.ShallowClone();//this code is returing Point3D Object instead of Node
}
catch (Exception)
{
throw;
}
finally
{
Console.ReadLine();
}
}
}
public interface IShallowCloneable<T>
{
T ShallowClone();
}
public class Point2D : IShallowCloneable<Point2D>
{
public int X { get; set; }
public int Y { get; set; }
public Point2D()
{
}
public Point2D(int x, int y)
{
X = x;
Y = y;
}
public Point2D ShallowClone()
{
return new Point2D(X, Y);
}
}
public class Point3D : Point2D, IShallowCloneable<Point3D>
{
public int Z { get; set; }
public Point3D()
{
}
public Point3D(int x, int y,int z):base(x,y)
{
Z = z;
}
new public Point3D ShallowClone()
{
return new Point3D(X, Y,Z);
}
}
public class Node:Point3D, IShallowCloneable<Node>
{
public int Id { get; set; }
public Node()
{
}
public Node(int id,int x, int y, int z):base(x,y,z)
{
Id = id;
}
Node IShallowCloneable<Node>.ShallowClone()
{
return new Node(Id,X, Y, Z);
}
}
}
Because for Node you've implemented IShallowCloneable<Node> as explicit interface, so it will work only if you cast to it:
// prints Node
Console.WriteLine(((IShallowCloneable<Node>)node).ShallowClone().GetType().Name);
If you want it to behave as Point3D you need to implement it as you do there ( hiding inherited Point2D implementation with new keyword).
so if you want to use ShallowClone method Explicitly without changing Class
var node = new Node(1, 0, 0, 0);
var node2 = ((IShallowCloneable<Node>)node).ShallowClone();
or if you want implicit implementation you have to Modify ShallowClone() method implementation in Node Class like this
new public Node ShallowClone()
{
return new Node(Id, X, Y, Z);
}
Additional Reference
C# Interfaces. Implicit implementation versus Explicit implementation

Covariant Collection not working

Sorry if the question is redundant, but I couldn't find a solution for my particular case.
Please consider this block of code:
public interface IPoint {}
public class GazePoint : IPoint {}
public Point AvgPoint(IEnumerable<IPoint> locations) {}
List<GazePoint> gazePoints = new List<GazePoint>();
//...
// this doesn't work:
Point avg = AvgPoint(gazePoints);
Could you please explain why it doesn't work (I was assuming C# 4.0 has solved this issue) and how can I change the signature of AvgPoint() method to make it possible to receive different implementations of IPoint. (I don't want to cast gazePoints collection to another type of collection, because it is in a big loop, and I'm concern about the performance.
[Update]: I had defined GazePoint as struct, and that was the source of problem. Still, I don't know why struct is not working here.
I'm not sure what the exact problem is you're having, but here's how it worked for me:
First, some actual class implementations:
public interface IPoint
{
int X { get; set; }
int Y { get; set; }
}
public class Point : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public Point()
{
}
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public class GazePoint : IPoint
{
public int X { get; set; }
public int Y { get; set; }
public GazePoint()
{
}
public GazePoint(int x, int y)
{
X = x;
Y = y;
}
}
Then an actual AvgPoint method implementation:
public static Point AvgPoint(IEnumerable<IPoint> locations)
{
if (locations == null || !locations.Any()) return new Point(0, 0);
return new Point((int) locations.Average(l => l.X),
(int) locations.Average(l => l.Y));
}
And finally a few tests:
public static void Main()
{
var points = new List<Point>
{
new Point(1, 2),
new Point(3, 4)
};
var gazePoints = new List<GazePoint>
{
new GazePoint(1, 2),
new GazePoint(3, 4)
};
Point avgPoint = AvgPoint(points);
Point avgGazePoint = AvgPoint(gazePoints);
Console.WriteLine("Average Point = {0}, {1}", avgPoint.X, avgPoint.Y);
Console.WriteLine("Average GazePoint = {0}, {1}", avgGazePoint.X, avgGazePoint.Y);
}
If your goal is to have the method return the average in the same type that was passed in, you can make it generic like so:
public static T AvgPoint<T>(IEnumerable<T> locations) where T : IPoint, new()
{
if (locations == null || !locations.Any()) return new T {X = 0, Y = 0};
return new T {X = (int) locations.Average(l => l.X),
Y = (int) locations.Average(l => l.Y)};
}

Generic IComparer for sorting different objects in different properties

I'm trying to sort an array of objects with IComparer.
I wrote the code but it works only with the particular object. e.g.:
for this class
public class Cars
{
public string Name { get; set; }
public string Manufacturer { get; set; }
public int Year { get; set; }
public Cars(string name, string manufacturer, int year)
{
Name = name;
Manufacturer = manufacturer;
Year = year;
}
}
My code looks like:
class MySort
{
public class SortByYears : IComparer
{
int IComparer.Compare(Object x, Object y)
{
Cars X = (Cars)x, Y = (Cars)y;
return (X.Year.CompareTo(Y.Year));
}
}
public class SortByName : IComparer
{
int IComparer.Compare(Object x, object y)
{
Cars X = (Cars)x, Y = (Cars)y;
return (X.Name.CompareTo(Y.Name));
}
}
public class SortByManyfacturer : IComparer
{
int IComparer.Compare(object x, object y)
{
Cars X = (Cars)x, Y = (Cars)y;
return (X.Manufacturer.CompareTo(Y.Manufacturer));
}
}
}
But if I add another class with different properties it will be useless.
So is there any chance to modify this code so that it worked for objects with different properties?
class SortComparer<T> : IComparer<T>
{
private PropertyDescriptor PropDesc = null;
private ListSortDirection Direction =
ListSortDirection.Ascending;
public SortComparer(object item,string property,ListSortDirection direction)
{
PropDesc = TypeDescriptor.GetProperties(item)[property];
Direction = direction;
}
int IComparer<T>.Compare(T x, T y)
{
object xValue = PropDesc.GetValue(x);
object yValue = PropDesc.GetValue(y);
return CompareValues(xValue, yValue, Direction);
}
private int CompareValues(object xValue, object yValue,ListSortDirection direction)
{
int retValue = 0;
if (xValue is IComparable) // Can ask the x value
{
retValue = ((IComparable)xValue).CompareTo(yValue);
}
else if (yValue is IComparable) //Can ask the y value
{
retValue = ((IComparable)yValue).CompareTo(xValue);
}
// not comparable, compare String representations
else if (!xValue.Equals(yValue))
{
retValue = xValue.ToString().CompareTo(yValue.ToString());
}
if (direction == ListSortDirection.Ascending)
{
return retValue;
}
else
{
return retValue * -1;
}
}
}
Calling code:
Assuming a list named lst:
lst.Sort(new SortComparer<Cars>(lst[0],"YourPropertyName",ListSortDirection.Ascending));
You may leverage the Create method of Comparer<T> which takes a Comparison delegate and returns Comparer<T>.
var carnameComparer = Comparer<Cars>.Create((x, y) => x.Year.CompareTo(y.Year));
var carManufacturerComparer = Comparer<Cars>.Create((x, y) => x.Manufacturer.CompareTo(y.Manufacturer));
and for another type
var carsComparer = Comparer<SomeType>.Create((x, y) => x.SomeProperty.CompareTo(y.SomeProperty));
If you're in prior to .Net4.5 you can use the following CreateComparer method.
private static IComparer<T> CreateComparer<T>(Comparison<T> comparison)
{
return new ComparisonComparer<T>(comparison);
}
public class ComparisonComparer<T> : IComparer<T>
{
private Comparison<T> comparison;
public ComparisonComparer(Comparison<T> comparison)
{
if (comparison == null)
{
throw new ArgumentNullException("comparison");
}
this.comparison = comparison;
}
public int Compare(T x, T y)
{
return comparison(x, y);
}
}
Use an interface and use generic IComparer Interface instead of IComparer
public interface IObjectWithNameProperty
{
string Name {get; set;}
}
public class MyNameComparer : IComparer<IObjectWithNameProperty>
{
public int Compare(IObjectWithNameProperty x, IObjectWithNameProperty y)
{
...
}
}
public class Car: IObjectWithNameProperty
{
public string Name {get;set;}
...
}
public class Dog: IObjectWithNameProperty
{
public string Name {get;set;}
...
}
Here is another take based on terrybozzio's.
public class PropertyComparer<T> : IComparer<T> where T : new()
{
private PropertyDescriptor PropDesc = null;
private ListSortDirection Direction = ListSortDirection.Ascending;
public PropertyComparer(string property, ListSortDirection direction)
{
T item = new T();
PropDesc = TypeDescriptor.GetProperties(item)[property];
Direction = direction;
Type interfaceType = PropDesc.PropertyType.GetInterface("IComparable");
if (interfaceType == null && PropDesc.PropertyType.IsValueType)
{
Type underlyingType = Nullable.GetUnderlyingType(PropDesc.PropertyType);
if (underlyingType != null)
{
interfaceType = underlyingType.GetInterface("IComparable");
}
}
if (interfaceType == null)
{
throw new NotSupportedException("Cannot sort by " + PropDesc.Name +
". This" + PropDesc.PropertyType.ToString() +
" does not implement IComparable");
}
}
int IComparer<T>.Compare(T x, T y)
{
object xValue = PropDesc.GetValue(x);
object yValue = PropDesc.GetValue(y);
IComparable comparer = (IComparable)xValue;
if (Direction == ListSortDirection.Ascending)
{
return comparer.CompareTo(yValue);
}
else
{
return -1 * comparer.CompareTo(yValue);
}
}
}
The cleanest way is to define an interface that both object implement, then use that in the comparison. Otherwise you're going to have a mess of case statements depending on the
possible combination of objects:
public class SortByYears : IComparer
{
int IComparer.Compare(Object x, Object y)
{
if(x is Cars)
{
Cars X = (Cars)x
if(y is Cars)
{
Y = (OtherCars)y;
return (X.Year.CompareTo(Y.Year));
if(y is OtherCars)
{
Y = (OtherCars)y;
return (X.Year.CompareTo(Y.Year));
}
}
if(x is OtherCars)
{
... repeat upper block
}
}
}
Another approach would be using the generic IComparer interface and lambda expressions.
class CarComparer<T> : IComparer<Car> where T : IComparable<T>
{
private readonly Func<Car, T> _sortExpression;
public CarComparer(Func<Car, T> sortExpression)
{
_sortExpression = sortExpression;
}
public int Compare(Car x, Car y)
{
return _sortExpression(x).CompareTo(_sortExpression(y));
}
}
This class compares the Car's property passed in parameter in the constructor.
// Sort the cars by name
var nameCarComparer = new CarComparer<string>(car => car.Name);
Array.Sort(myArray, nameCarComparer);

How to implement a simple generic comparison in C#

I am just begining to explore the basic of using generics and would have thought i could implement this simple pattern to solve a typical problem in my daily use. I have spent days searching for a simple example. I can find examples for looking for .Equals, but not much past that. I want to be able to instantiate with things like:
Spec<double> voltageSpec;
Spec<int> cyclesSpec;
Spec<myClass> fishInTheOceanSpec;
then be able to :
bool isGood = voltageSpec.inSpec(5.0);
bool isGood cyclesSpec.inSpec(2);
bool isGood fishInTheOceanSpec.( new myClass(20));
My attempt is shown below.
/// <summary>
/// Generic object to hold a specification i.e min and max limits.
/// Implements a method to determin if a value is between limits.
/// </summary>
public class Spec<T> : IComparer<T>
{
public Spec()
{
Min = default(T);
Max = default(T);
}
public T Min { get; set; }
public T Max { get; set; }
public bool inSpec(T Value)
{
if ((Comparer<T>.Default.Compare(Value, this.Max) <= 0) &
(Comparer<T>.Default.Compare(Value, this.Min) >= 0))
return true;
else
return false;
}
public int Compare(T x, T y)
{
if (x == y) return 0;
if (x < y) return -1;
if (x > y) return 1;
}
public Spec<T> Copy()
{
return (Spec<T>)this.MemberwiseClone();
}
}
I would refactor as follows - make T be responsible to provide the comparison - this works already for primitive types, your custom classes just have to implement IComparable<T>:
public class Spec<T> where T : IComparable<T>
{
public Spec()
{
Min = default(T);
Max = default(T);
}
public T Min { get; set; }
public T Max { get; set; }
public bool inSpec(T Value)
{
if(Value.CompareTo(this.Max) <=0 &&
Value.CompareTo(this.Min) >=0)
return true;
else
return false;
}
public Spec<T> Copy()
{
return (Spec<T>)this.MemberwiseClone();
}
}
Declare your class like this
public class Spec<T> : IComparer<T>
where T : IComparable<T>
{
....
}
Then you can apply CompareTo to T
public int Compare(T x, T y)
{
return x.CompareTo(y);
}
Basic types like int, double or string do implement IComparable<T>.

Categories

Resources