IComparer and Sort

  • Thread starter Thread starter coder316
  • Start date Start date
coder316 said:
In the following code I am declaring and useing an interface.Its
pretty strait forward.

Except that it's not. Yes, you are declaring and implementing an
interface. But you're not _using_ it at all.

A better code example would look like this:

namespace Interface2
{
interface MyInterface
{
int AddNumbers(int x, int y);
}

class Program
{
static void Main(string[] args)
{
MyInterface myinterface = new UseInterface();
Console.WriteLine(myinterface.AddNumbers(2,2));
Console.ReadLine();
}
}

class UseInterface : MyInterface
{

private int MyInterface.AddNumbers(int a, int b)
{
int c;
return c = a + b;
}

}
}

I've made a couple of changes, one very important, one not so important:

-- very important: the variable type is now "MyInterface" rather
than "UseInterface". Typed as the actual object type, you'll have
access to all public members of the type, regardless of interface
implementations. So your previous example wasn't very interesting, as
the interface itself could have been omitted altogether without changing
the behavior of the code (or even whether it would compile!). With the
variable types as "MyInterface", you can add all sorts of other stuff to
"UseInterface", but none of that will show up in the variable
"MyInterface"...you'll still only be able to call the AddNumbers() method.

-- less important: I made the interface implementation "explicit".
That is, the method name is qualified with the interface name. This
means that the _only_ way to call the method is via the interface. You
can't call it with a variable of type "UseInterface"; the expression for
the reference _has_ to be "MyInterface".

The important thing to keep in mind is that when a class implements an
interface, other code can access all the members of the class _that are
included as implementation of that interface_ using only the interface
type itself, without knowing anything else about the class.

That's what happens with the Sort() method. It doesn't need to know
anything about the elements of the array, except that they implement the
IComparable interface. Knowing that, Sort() can simply cast each object
to IComparable, and then call the CompareTo() method declared in the
IComparable interface.

Pete
 
Tony said:
Hello!

At the end is a complete program that give the call stack shown below
I use Red Gate's .NET Reflector on my assembly.
I can click in the disassembler and follow the sequence of calls that is
being made up to this call to QuickSort
mscorlib.dll!System.Array.SorterObjectArray.QuickSort(int left = 0, int
right = 4) + 0x40 bytes
I can't understand how this call to SwapIfGreaterWithItems can be made from
the QuickSort ?

Why can't you understand that? It's no different from any other method
call. Why _wouldn't_ the QuickSort() method be able to call the
SwapIfGreaterWithItems() method?
And as the last question I have tried to find the call to CompareTo using
Red Gate's .NET Reflector on my assembly but
I can't find it

Why not? The stack trace shows you exactly where to find it: in the
System.Collections.Comparer.Compare() method. And when I use Reflector
to examine that method, this is what I see:

public int Compare(object a, object b)
{
if (a == b)
{
return 0;
}
if (a == null)
{
return -1;
}
if (b == null)
{
return 1;
}
if (this.m_compareInfo != null)
{
string str = a as string;
string str2 = b as string;
if ((str != null) && (str2 != null))
{
return this.m_compareInfo.Compare(str, str2);
}
}
IComparable comparable = a as IComparable;
if (comparable == null)
{
throw new
ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
}
return comparable.CompareTo(b);
}

Note the very last lines, starting with the declaration of "comparable".
After all other possibilities are exhausted, the method checks to see
if the object type implements IComparable, and if it does, it calls the
IComparable.CompareTo() method.
int Compare(object a, object b)
{
return ((IComparable)a).CompareTo(b);
}

I just wonder if some code is hidden in some way so I have not chance to see
it that
would explain why I can't find the call to CompareTo

It's not hidden at all.

Pete
 
coder316 said:
In the following code I am declaring and useing an interface.Its
pretty strait forward.

Except that it's not.  Yes, you are declaring and implementing an
interface.  But you're not _using_ it at all.

A better code example would look like this:

namespace Interface2
{
     interface MyInterface
     {
         int AddNumbers(int x, int y);
     }

     class Program
     {
         static void Main(string[] args)
         {
             MyInterface myinterface = new UseInterface();
             Console.WriteLine(myinterface.AddNumbers(2,2));
             Console.ReadLine();
         }
     }

     class UseInterface : MyInterface
     {

         private int MyInterface.AddNumbers(int a, int b)
         {
             int c;
             return c = a + b;
         }

     }

}

I've made a couple of changes, one very important, one not so important:

     -- very important: the variable type is now "MyInterface" rather
than "UseInterface".  Typed as the actual object type, you'll have
access to all public members of the type, regardless of interface
implementations.  So your previous example wasn't very interesting, as
the interface itself could have been omitted altogether without changing
the behavior of the code (or even whether it would compile!).  With the
variable types as "MyInterface", you can add all sorts of other stuff to
"UseInterface", but none of that will show up in the variable
"MyInterface"...you'll still only be able to call the AddNumbers() method..

     -- less important: I made the interface implementation "explicit".
  That is, the method name is qualified with the interface name.  This
means that the _only_ way to call the method is via the interface.  You
can't call it with a variable of type "UseInterface"; the expression for
the reference _has_ to be "MyInterface".

The important thing to keep in mind is that when a class implements an
interface, other code can access all the members of the class _that are
included as implementation of that interface_ using only the interface
type itself, without knowing anything else about the class.

That's what happens with the Sort() method.  It doesn't need to know
anything about the elements of the array, except that they implement the
IComparable interface.  Knowing that, Sort() can simply cast each object
to IComparable, and then call the CompareTo() method declared in the
IComparable interface.

Pete

Great Explanations,
Thank You!!!
 
Except that it's not.  Yes, you are declaring and implementing an
interface.  But you're not _using_ it at all.
A better code example would look like this:
namespace Interface2
{
     interface MyInterface
     {
         int AddNumbers(int x, int y);
     }
     class Program
     {
         static void Main(string[] args)
         {
             MyInterface myinterface = new UseInterface();
             Console.WriteLine(myinterface.AddNumbers(2,2));
             Console.ReadLine();
         }
     }
     class UseInterface : MyInterface
     {
         private int MyInterface.AddNumbers(int a, int b)
         {
             int c;
             return c = a + b;
         }
     }

I've made a couple of changes, one very important, one not so important:
     -- very important: the variable type is now "MyInterface" rather
than "UseInterface".  Typed as the actual object type, you'll have
access to all public members of the type, regardless of interface
implementations.  So your previous example wasn't very interesting, as
the interface itself could have been omitted altogether without changing
the behavior of the code (or even whether it would compile!).  With the
variable types as "MyInterface", you can add all sorts of other stuff to
"UseInterface", but none of that will show up in the variable
"MyInterface"...you'll still only be able to call the AddNumbers() method.
     -- less important: I made the interface implementation "explicit".
  That is, the method name is qualified with the interface name.  This
means that the _only_ way to call the method is via the interface.  You
can't call it with a variable of type "UseInterface"; the expression for
the reference _has_ to be "MyInterface".
The important thing to keep in mind is that when a class implements an
interface, other code can access all the members of the class _that are
included as implementation of that interface_ using only the interface
type itself, without knowing anything else about the class.
That's what happens with the Sort() method.  It doesn't need to know
anything about the elements of the array, except that they implement the
IComparable interface.  Knowing that, Sort() can simply cast each object
to IComparable, and then call the CompareTo() method declared in the
IComparable interface.

Great Explanations,
Thank You!!!- Hide quoted text -

- Show quoted text -

One other thing is that all of the online examples and books i"m
working with (Pro C#2008 and the .net Framework) just implement the
interface they way I showed in my example.
They use it as a "This must implement that" . I havent seen where they
just set the reference to the Interface type so that you can only use
the methods implemented by the interface. I like it, but I havent seen
it before.
Thanks
 
Below is method QuickSort located in System.Array.SorterObjectArray
mscorlib.dll!System.Array.SorterObjectArray.QuickSort(int left = 0, int
right = 4) + 0x40 bytes

According to the stack trace should a call to SwapIfGreaterWithItems be made
from QuickSort
mscorlib.dll!System.Array.SorterObjectArray.SwapIfGreaterWithItems(int a =
0, int b = 2) + 0x59 bytes

There is no call to SwapIfGreaterWithItems in this method QuickSort.
These must be something that I have missed here ?

internal void QuickSort(int left, int right)
{
do
{
int i = left;
int j = right;
object y = this.keys[Array.GetMedian(i, j)];
do
{
try
{
while (this.comparer.Compare(this.keys, y) < 0)
{
i++;
}
while (this.comparer.Compare(y, this.keys[j]) < 0)
{
j--;
}
}
catch (IndexOutOfRangeException)
{
throw new ArgumentException();
}
catch (Exception)
{
throw new InvalidOperationException();
}
if (i > j)
{
break;
}
if (i < j)
{
object obj3 = this.keys;
this.keys = this.keys[j];
this.keys[j] = obj3;
if (this.items != null)
{
object obj4 = this.items;
this.items = this.items[j];
this.items[j] = obj4;
}
}
i++;
j--;
}
while (i <= j);
if ((j - left) <= (right - i))
{
if (left < j)
{
this.QuickSort(left, j);
}
left = i;
}
else
{
if (i < right)
{
this.QuickSort(i, right);
}
right = j;
}
}
while (left < right);
}
 
Tony said:
Below is method QuickSort located in System.Array.SorterObjectArray
mscorlib.dll!System.Array.SorterObjectArray.QuickSort(int left = 0, int
right = 4) + 0x40 bytes

Obtained from where? Because that's not what I see in Reflector, nor is
it what I see in the original .NET source code. Both versions I look at
have three calls to SwapIfGreaterWithItems(), just after the statement
where GetMedian() is called, and just before the start of the do/while
statement.

Here's an excerpt:

internal void QuickSort(int left, int right)
{
do
{
int low = left;
int hi = right;
int median = Array.GetMedian(low, hi);
this.SwapIfGreaterWithItems(low, median);
this.SwapIfGreaterWithItems(low, hi);
this.SwapIfGreaterWithItems(median, hi);
object y = this.keys[median];
do
{

(Interestingly, it's obvious that the .NET source I'm looking at is not
current for this module, because the source I have has variable names
"i" and "j", rather than the "low" and "hi" that are seen in Reflector).

If you're not seeing the call to SwapIfGreaterWithItems(), it's simply
because you're looking in the wrong place.

Pete
 
This was strange.
I use Red Gates's .NET Reflector.
Version on mscorlib.dll is 4f9ab708-48fd-43f4-869a-008e7cb40aa8

I get QuickSort in this way.
1. Select mscorlib.dll
2. Select System
3. Select SorterObjectArray
4. Select QuickSort

Here is the text that can be read at the bottom left

internal void QuickSort(int left, int right);
Declaring Type: System.Array+SorterObjectArray
Assembly: mscorlib, Version=2.0.0.


I get the same result if I start form main using reflector and click until I
reach QuickSort
Have you any idea why I don't see the correct source code in Reflector

//Tony
 
Tony said:
This was strange.
I use Red Gates's .NET Reflector.
Version on mscorlib.dll is 4f9ab708-48fd-43f4-869a-008e7cb40aa8

That's a GUID. Technically, it's the unique number describing the
assembly, but IMHO the version information is more useful/understandable.

On my computer (Windows 7, .NET 3.5), the assembly version is
"2.0.50727.4927".
I get QuickSort in this way.
1. Select mscorlib.dll
2. Select System
3. Select SorterObjectArray

Your step #3 shouldn't work. You should need to select Array first.
4. Select QuickSort

Here is the text that can be read at the bottom left

internal void QuickSort(int left, int right);
Declaring Type: System.Array+SorterObjectArray
Assembly: mscorlib, Version=2.0.0.


I get the same result if I start form main using reflector and click until I
reach QuickSort
Have you any idea why I don't see the correct source code in Reflector

No, sorry I don't. At the very least, if you run Reflector on the exact
same computer where you produced the stack trace showing the call to
SwapIfGreaterWithItems(), it should produce a view of the assembly that
is consistent with the stack trace. The only reason it would/should be
different is if you are looking at a different assembly in Reflector
than you are using in your program.

That _could_ happen if you have multiple versions of the .NET assemblies
lying around on your computer. But _that_ shouldn't happen normally. A
person would have to intentionally clutter their computer like that, and
surely that's something you'd remember having done?

All that said, you can verify the exact file being used in both
Reflector and in the debugger. In Reflector, just select the DLL
itself, and the information pane will show you the file path.

In the debugger, it might be a little more tricky. You can easily see
the source of each DLL loaded in the process by, while debugging,
displaying the "Modules" debug window. But normally, a DLL like
mscorlib.dll is executed from the GAC and this won't match directly with
the path shown in Reflector.

Of course, if it turns out you're loading a version of mscorlib.dll
that's not in the GAC, then that's probably your problem right there.
:) But, if it's in the GAC, AFAIK the best you can do is look at the
entry in the .NET Framework 2.0 Configuration MMC plug-in, but the only
semi-unique identifier that goes with an assembly in the GAC is the
public key token, and I don't recall whether that changes each version
(for sure, it's not unique to the assembly, because lots of other
assemblies share the same public key token...but it might be unique for
the framework version).

Anyway, that's a long way of saying, no...I don't know why on your
computer the stack trace doesn't match what you see in Reflector.

Pete
 
Hello!

Now I get the same contents as you have in QuickSort.
In the References in the solution Expolere I used this location
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
so I use this location in reflector now.

Earlier I used this location in reflector
Location:
%ProgramFiles%\Microsoft.NET\SDK\CompactFramework\v2.0\Debugger\BCL\

I just wonder how does the program Red Gate's Reflector decide which
assembly to use when there
exist several assembly for example System.dll and mscorlib.dll and these
assembly have differerent contents.

//Tony
 
Hello!

Now I get the same contents as you have in QuickSort.
In the References in the solution Expolere I used this location
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
so I use this location in reflector now.

Earlier I used this location in reflector
Location:
%ProgramFiles%\Microsoft.NET\SDK\CompactFramework\v2.0\Debugger\BCL\

I just wonder how does the program Red Gate's Reflector decide which
assembly to use when there
exist several assembly for example System.dll and mscorlib.dll and these
assembly have differerent contents.

//Tony
 
Hello!

Now I get the same contents as you have in QuickSort.
In the References in the solution Expolere I used this location
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
so I use this location in reflector now.

Earlier I used this location in reflector
Location:
%ProgramFiles%\Microsoft.NET\SDK\CompactFramework\v2.0\Debugger\BCL\

I just wonder how does the program Red Gate's Reflector decide which
assembly to use when there
exist several assembly for example System.dll and mscorlib.dll and these
assembly have differerent contents.

//Tony
 
Hello!

Now I get the same contents as you have in QuickSort.
In the References in the solution Expolere I used this location
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
so I use this location in reflector now.

Earlier I used this location in reflector
Location:
%ProgramFiles%\Microsoft.NET\SDK\CompactFramework\v2.0\Debugger\BCL\

I just wonder how does the program Red Gate's Reflector decide which
assembly to use when there
exist several assembly for example System.dll and mscorlib.dll and these
assembly have differerent contents.

//Tony
 
Back
Top