about using compare with generics

T

Tony

Hello!

Below I have a complete working program.with some simple classes one of
these is a generic class.

Now If I want to implement functionallity so I can compare animal with each
other or with other animal
for example compare a Chicken with a Cow in some way or a Cow with another
Cow

How is that done?
My attempted strategy is to set a constraint on T in such a way that T is
required to implement the
CompareTo method that exist in IEnumerable<T>
So I change the class definition header for the generic class from this
definition
public class Farm<T> : IEnumerable<T> where T : Animal
to this
public class Farm<T> : IEnumerable<T> where T : Animal, IEnumerable<T>
I then let Cow, Chicken and SuperCow inherit from IEnumarable
and then implement CompareTo in each class which is Cow, Chicken and
SuperCow

I tried this but run into several problems so my strategy was perhaps
complete
wrong.
So how is the correct way of doing this?


using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace ConsoleApplication15
{
class Program
{
static void Main(string[] args)
{
Farm<Animal> farm = new Farm<Animal>();
farm.Animal.Add(new Cow("Jack"));
farm.Animal.Add(new Chicken("Vera"));
farm.Animal.Add(new Chicken("Sally"));
farm.Animal.Add(new SuperCow("Kevin"));

farm.MakeNoises();

Farm<Cow> dairyFarm = farm.GetCows();
dairyFarm.FeedTheAnimals();

foreach (Cow cow in dairyFarm)
if (cow is SuperCow)
((SuperCow)cow).Fly();

Farm<Animal> newFarm = farm + dairyFarm;
Console.ReadKey();
}
}

public abstract class Animal
{
string name;
public Animal(string name)
{ this.name = name; }

public string Name
{ get { return name; } }

public abstract void MakeANoise();
public abstract void Feed();
}

public class Chicken : Animal
{
public Chicken(string name) : base(name) {}

public override void MakeANoise()
{ Console.WriteLine("{0} says 'cluck!'", Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(Chicken)", Name); }
}

public class Cow : Animal
{
public Cow(string name) : base(name) {}

public override void MakeANoise()
{ Console.WriteLine("{0} says 'moo!'", Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(Cow)", Name); }
}

public class SuperCow : Cow
{
public SuperCow(string name) : base(name) {}

public void Fly()
{ Console.WriteLine("{0} is flying!", Name); }

public override void MakeANoise()
{ Console.WriteLine("{0} says 'here I come to save the day!'",
Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(SuperCow)", Name); }
}

public class Farm<T> : IEnumerable<T> where T : Animal
{
private List<T> animals = new List<T>();

public List<T> Animal
{ get { return animals; } }

public IEnumerator<T> GetEnumerator()
{ return animals.GetEnumerator(); }

IEnumerator IEnumerable.GetEnumerator()
{ return animals.GetEnumerator(); }


public void MakeNoises()
{
foreach (T animal in animals)
animal.MakeANoise();
}

public void FeedTheAnimals()
{
foreach (T animal in animals)
animal.Feed();
}

public Farm<Cow> GetCows()
{
Farm<Cow> cowFarm = new Farm<Cow>();
foreach (T animal in animals)
if (animal is Cow)
cowFarm.animals.Add(animal as Cow);
return cowFarm;
}

public static Farm<T> operator +(Farm<T> farm1, Farm<T> farm2)
{
Farm<T> result = new Farm<T>();
foreach (T animal in farm1)
result.Animal.Add(animal);

foreach (T animal in farm2)
if (!result.Animal.Contains(animal))
result.Animal.Add(animal);
return result;
}

public static implicit operator Farm<Animal>(Farm<T> farm)
{
Farm<Animal> result = new Farm<Animal>();

foreach(T animal in farm)
result.Animal.Add(animal);
return result;
}
}
}

//Tony
 
M

Marc Gravell

First - what exactly do you mean by "compare" here? What would a
comparsion do? You state: "for example compare a Chicken with a Cow in
some way"
Well, in which way? If you had an actual cow, and an actual chicken, and
had to "compare" them, then what would you do? And ditto with 2 cows.

You talk about CompareTo; this is an ordering comparsion, as defined by
IComparable[<T>] and IComparer[<T>] (not IEnumerable[<T>]). If you want
to order 2 cows, then you could look at having Cow : IComparable<Cow>,
and either adding "T : IComparable<Cow>" as a constraint somewhere, or
(easier) using Comparer<Cow>.Default or Comparer<T>.Default.

In contrast, comparing a Cow and a Chicken is more complex... what if
Cow : IComprable<Chicken> and Chicken : IComparable<Cow>? Which would
you choose? And if neither? I think the first step here is understanding
the problem...

Marc
 
T

Tony

Hello!

Here I have put a constraint on the generic class farm as to
force T to implement the IComparable interface.

I just wonder can this be done in a better way because I don't have strong
types
in CompareTo method which is impemented in all classes except the abstract
class ?

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace ConsoleApplication15
{
class Program
{
static void Main(string[] args)
{
Farm<Animal> farm = new Farm<Animal>();
farm.Animal.Add(new Cow("Jack"));
farm.Animal.Add(new Chicken("Vera"));
farm.Animal.Add(new Chicken("Sally"));
farm.Animal.Add(new SuperCow("Kevin"));

farm.MakeNoises();

Farm<Cow> dairyFarm = farm.GetCows();
dairyFarm.FeedTheAnimals();

foreach (Cow cow in dairyFarm)
if (cow is SuperCow)
((SuperCow)cow).Fly();

Farm<Animal> newFarm = farm + dairyFarm;
Console.ReadKey();
}
}

public abstract class Animal: IComparable
{
string name;
public Animal(string name)
{ this.name = name; }

public string Name
{ get { return name; } }

public abstract void MakeANoise();
public abstract void Feed();
public abstract int CompareTo(object obj);
}

public class Chicken : Animal, IComparable
{
public Chicken(string name) : base(name) {}

public override void MakeANoise()
{ Console.WriteLine("{0} says 'cluck!'", Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(Chicken)", Name); }

public override int CompareTo(Object obj)
{
return 0;
}
}

public class Cow : Animal, IComparable
{
public Cow(string name) : base(name) {}

public override void MakeANoise()
{ Console.WriteLine("{0} says 'moo!'", Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(Cow)", Name); }

public override int CompareTo(object obj)
{
return 0;
}

}

public class SuperCow : Cow, IComparable
{

public SuperCow(string name) : base(name) {}

public void Fly()
{ Console.WriteLine("{0} is flying!", Name); }

public override void MakeANoise()
{ Console.WriteLine("{0} says 'here I come to save the day!'",
Name); }

public override void Feed()
{ Console.WriteLine("{0} has been feed(SuperCow)", Name); }

public override int CompareTo(object obj)
{
return 0;
}
}

public class Farm<T> : IEnumerable<T>
where T : Animal, IComparable
{
private List<T> animals = new List<T>();

public List<T> Animal
{ get { return animals; } }

public IEnumerator<T> GetEnumerator()
{ return animals.GetEnumerator(); }

IEnumerator IEnumerable.GetEnumerator()
{ return animals.GetEnumerator(); }


public void MakeNoises()
{
foreach (T animal in animals)
animal.MakeANoise();
}

public void FeedTheAnimals()
{
foreach (T animal in animals)
animal.Feed();
}

public Farm<Cow> GetCows()
{
Farm<Cow> cowFarm = new Farm<Cow>();
foreach (T animal in animals)
if (animal is Cow)
cowFarm.animals.Add(animal as Cow);
return cowFarm;
}

public static Farm<T> operator +(Farm<T> farm1, Farm<T> farm2)
{
Farm<T> result = new Farm<T>();
foreach (T animal in farm1)
result.Animal.Add(animal);

foreach (T animal in farm2)
if (!result.Animal.Contains(animal))
result.Animal.Add(animal);
return result;
}

public static implicit operator Farm<Animal>(Farm<T> farm)
{
Farm<Animal> result = new Farm<Animal>();

foreach(T animal in farm)
result.Animal.Add(animal);
return result;
}
}
}

//Tony

Marc Gravell said:
First - what exactly do you mean by "compare" here? What would a
comparsion do? You state: "for example compare a Chicken with a Cow in
some way"
Well, in which way? If you had an actual cow, and an actual chicken, and
had to "compare" them, then what would you do? And ditto with 2 cows.

You talk about CompareTo; this is an ordering comparsion, as defined by
IComparable[<T>] and IComparer[<T>] (not IEnumerable[<T>]). If you want
to order 2 cows, then you could look at having Cow : IComparable<Cow>,
and either adding "T : IComparable<Cow>" as a constraint somewhere, or
(easier) using Comparer<Cow>.Default or Comparer<T>.Default.

In contrast, comparing a Cow and a Chicken is more complex... what if
Cow : IComprable<Chicken> and Chicken : IComparable<Cow>? Which would
you choose? And if neither? I think the first step here is understanding
the problem...

Marc
 
G

Göran Andersson

Tony said:
Hello!

Below I have a complete working program.with some simple classes one of
these is a generic class.

Now If I want to implement functionallity so I can compare animal with each
other or with other animal
for example compare a Chicken with a Cow in some way or a Cow with another
Cow

How is that done?

First you have to specify what you want the comparison to do.

You can already compare the objects:

if (cow1 == cow2) { ... }

However, this comparison uses the default reference comparison, so that
is probably not what you want to do. So, you have to determine what it
is that you want to do.
My attempted strategy is to set a constraint on T in such a way that T is
required to implement the
CompareTo method that exist in IEnumerable<T>
So I change the class definition header for the generic class from this
definition
public class Farm<T> : IEnumerable<T> where T : Animal
to this
public class Farm<T> : IEnumerable<T> where T : Animal, IEnumerable<T>
I then let Cow, Chicken and SuperCow inherit from IEnumarable
and then implement CompareTo in each class which is Cow, Chicken and
SuperCow

I tried this but run into several problems so my strategy was perhaps
complete
wrong.

Making an animal enumerable doesn't seem to make much sense.

Looping through a cow sound wrong in so many ways... ;)

Besides, the IEnumerable<T> interface doesn't contain any CompareTo
method. Do you mean the IComparable<T> interface?
 
J

Jon Skeet [C# MVP]

Here I have put a constraint on the generic class farm as to
force T to implement the IComparable interface.

I just wonder can this be done in a better way because I don't have strong
types in CompareTo method which is impemented in all classes except the abstract
class ?

Well, for a start you should think about implementing IComparable<T>
instead of IComparable.

But also you really need to think about Marc's questions. How do you
want a Cow to compare to a Chicken? To another Cow? To an animal type
it's never seen before?

Jon
 
T

Tony

Hello!

If I want to implement the generic interface IComparable<T> instead of the
non-generic how is that done.
I can change the constraint on T to IComparable<T> instead of IComparable
in the class header definition for Farm.

But how do I write the class definition header for Cow for example now I
have
public class Cow : Animal, IComparable

I can't use any T here!

//Tony






"Jon Skeet [C# MVP]" <[email protected]> skrev i meddelandet
Here I have put a constraint on the generic class farm as to
force T to implement the IComparable interface.

I just wonder can this be done in a better way because I don't have strong
types in CompareTo method which is impemented in all classes except the abstract
class ?

Well, for a start you should think about implementing IComparable<T>
instead of IComparable.

But also you really need to think about Marc's questions. How do you
want a Cow to compare to a Chicken? To another Cow? To an animal type
it's never seen before?

Jon
 
J

Jon Skeet [C# MVP]

But how do I write the class definition header for Cow for example now I
have
public class Cow : Animal, IComparable

I can't use any T here!

You don't need to:

public class Cow : Animal, IComparable<Cow>

Jon
 
M

Marc Gravell

Looping through a cow sound wrong in so many ways... ;)

OK, that made me chuckle. However, I'm more concerned about SuperCow -
sounds like the marketing strategy for GE livestock...

Marc
 
M

Marc Gravell

The odd thing is; in a funny way there is a sensible OO answer here...
now, I don't like the idea of a Cow having to know how to compare itself
to a Duck a Cat and a Chicken, so IComparable[<T>] might not be easy.

However, lets assume we have a base-class Animal (Cow : Animal, etc); if
you just have a list of animals (List<Animal>), and an external
comparer, say:

Farmer : IComparer<Animal>

then you can get your Farmer instance (or whatever) to do the sort:

List<Animal> animals = ...
animals.Sort(farmer);

or ditto with Array.Sort<Animal>(animalArray, farmer);

Of course, the farmer still needs to have a sensible set of rules for
comparing each animal.

If you are comparing based on a property common to all Animal instances
- their Name, say (do farmers name their animals?), then it is much easier:

animals.Sort((x,y) => string.Compare(x.Name, y.Name));

Marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top