Parsing Generics Generically

O

O.B.

I'm trying to write an operation that sums the hash codes for object
that "may" be a generic list. Obviously the code below does not
compile. But it gets the point across of what I'm trying to
accomplish. Is there a way to cast the object to a List<>?


public int GetHashCodeSum(Object obj)
{
int rv = 0;
if (obj.GetType().IsGenericType &&
obj.GetType().GetGenericTypeDefinition() == typeof(List<>))
{
foreach(Object item in ((List<>)obj).ToArray())
{
rv += item.GetHashCode();
}
}
else
{
rv = obj.GetHashCode();
}
return rv;
}
 
P

Pavel Minaev

I'm trying to write an operation that sums the hash codes for object
that "may" be a generic list.  Obviously the code below does not
compile.  But it gets the point across of what I'm trying to
accomplish.  Is there a way to cast the object to a List<>?

public int GetHashCodeSum(Object obj)
{
  int rv = 0;
  if (obj.GetType().IsGenericType &&
      obj.GetType().GetGenericTypeDefinition() == typeof(List<>))
  {
    foreach(Object item in ((List<>)obj).ToArray())
    {
      rv += item.GetHashCode();
    }

First of all, for this specific case, you really should just use
IEnumerable (the non-generic one) - all collections are guaranteed to
implement it, as IEnumerable<T> extends IEnumerable. However,
sometimes you do really need that sort of thing - read on if you are
interested in a generic solution.

The most basic answer to your question: where isn't such a way,
because there are no objects of type List<> or compatible with it.
However, what you really want is to be able to just work with the
object. One obvious way is reflection - you can get the specific type
parameter for this object, and use it to call methods and handle
return values. Of course, this gets real messy real fast. A trick is
to separate the code that does generic processing into a separate
generic method; for example:

private int GetHashCodeTyped<T>(List<T> list)
{
int rv = 0;
foreach (var item in list) { rv += item.GetHashCode(); }
return rv;
}

Then, use reflection to invoke that method for various objects:

public int GetHashCodeSum(Object obj)
{
  int rv = 0;
  if (obj.GetType().IsGenericType &&
      obj.GetType().GetGenericTypeDefinition() == typeof(List<>))
  {
Type t = obj.GetType().GetGenericArguments()[0];
MethodInfo mdef = typeof(MyClass).GetMethod("GetHashCodeTyped",
BindingFlags.NonPublic);
MethodInfo m = mdef.MakeGenericMethod(t);
rv = (int)m.Invoke(this, new object[] { obj });
}
else
...
}

The advantage of this method is that no matter how complicated your
generic code is, it's still a few lines of reflection code to invoke
it, and from there you work with typed data.
 
O

O.B.

It seems to me that a more general and equally useful approach would be to  
simply see if "obj" implements IEnumerable.  For example:

     int GetHashCodeSum(object obj)
     {
         int rv = 0;
         IEnumerable enumerable = obj as IEnumerable;

         if (enumerable != null)
         {
             foreach (object item in enumerable)
             {
                 rv = rv * 37 + item.GetHashCode();
             }
         }
         else
         {
             rv = obj.GetHashCode();
         }

         return rv;
     }

This will handle any List<T> instance, and all sorts of other collections 
too.

Note the enhanced hash code calculation as well.  Simply summing hash  
codes can result in excessive collisions; better to multiply by a prime  
number before summing, so that each contribution is mathematically less  
dependent on the previous ones.

Pete

That is most sweet! Thanks for additional goodies.
 
U

usenet

I'm trying to write an operation that sums the hash codes for object
that "may" be a generic list.  Obviously the code below does not
compile.  But it gets the point across of what I'm trying to
accomplish.  Is there a way to cast the object to a List<>?

public int GetHashCodeSum(Object obj)
{
  int rv = 0;
  if (obj.GetType().IsGenericType &&
      obj.GetType().GetGenericTypeDefinition() == typeof(List<>))
  {
    foreach(Object item in ((List<>)obj).ToArray())
    {
      rv += item.GetHashCode();
    }
  }
  else
  {
    rv = obj.GetHashCode();
  }
  return rv;



}- Hide quoted text -

- Show quoted text -


As you have already been provided a working solution, I won't put one
here.

But, you do realize that summing two or more unique hashcodes does not
give another unique hashcode?

Example:
1 + 1 + 1 = 3
0 + 3 = 3
1 + 2 = 3

The number possible sums of x is called the partition of x. See
http://en.wikipedia.org/wiki/Partition_(number_theory).

Regards,

Sphere 10 Software (www.sphere10.com)
File Server Migrator - Copy Local Users and Groups
http://www.sphere10.com/fileservermigrator/
 
U

usenet

I'm trying to write an operation that sums the hash codes for object
that "may" be a generic list.  Obviously the code below does not
compile.  But it gets the point across of what I'm trying to
accomplish.  Is there a way to cast the object to a List<>?

public int GetHashCodeSum(Object obj)
{
  int rv = 0;
  if (obj.GetType().IsGenericType &&
      obj.GetType().GetGenericTypeDefinition() == typeof(List<>))
  {
    foreach(Object item in ((List<>)obj).ToArray())
    {
      rv += item.GetHashCode();
    }
  }
  else
  {
    rv = obj.GetHashCode();
  }
  return rv;



}- Hide quoted text -

- Show quoted text -


As you have already been provided a working solution, I won't put one
here.

But, you do realize that summing two or more unique hashcodes does not
give another unique hashcode?

Example:
1 + 1 + 1 = 3
0 + 3 = 3
1 + 2 = 3

See http://en.wikipedia.org/wiki/Partition_(number_theory).

Regards,

Sphere 10 Software (www.sphere10.com)
File Server Migrator - Copy Local Users and Groups
http://www.sphere10.com/fileservermigrator/
 
B

Ben Voigt [C++ MVP]

Angel J. Hernández M. said:
I think that this might help you, however I don't see the point of using
objects instead of generic types =)


public int GetHashCodeSum(object obj) {
int retval = 0;
Type objType = obj.GetType();

if (objType.IsGenericType &&
objType.GetGenericTypeDefinition() is IList) {

System.Type doesn't implement IList, this is always false
 
U

usenet

[...]
But, you do realize that summing two or more unique hashcodes does not
give another unique hashcode?

There is no way to combine hashcodes and guarantee unique results.  It's  
simply impossible.  Hash codes aren't meant to be unique.

It's true that simple summing isn't the best approach, but saying that  
that's because the result isn't guaranteed to be unique isn't helpful.  No  
approach provides such a guarantee.

Here's a quick method which takes advantage of string.GetHashCode() to
calculate the hashcode for any collection based on the hashcode of its
members.

static int CalculateCollectionHashCode(IEnumerable list) {
StringBuilder codeList = new StringBuilder();
foreach (object obj in list) {
codeList.Append(obj.GetHashCode());
}
return codeList.ToString().GetHashCode();
}



Regards,

Sphere 10 Software (www.sphere10.com)
File Server Migrator - Industrial Strength File Copier
http://www.sphere10.com/fileservermigrator/
 
U

usenet

That code is a very bad idea, for at least a couple of reasons:

No, it's a very good idea. The code is just a basis to be expanded.
The cleverness is in using the String.GetHashCode() which is already
implemented to generate unique as possible hashcodes. See MSDN
documentation for more info.
     -- It can potentially consume a very large amount of memory
     -- You have no guarantee that String.GetHashCode() is going to 
incorporate every value you add to the string

It goes without saying that the snippet I gave is a basis to be
expanded. Your issues can be easily solved by adding boundary
conditions for excessively large collections (i.e. just stop break out
of loop if gone past 5000 hashcodes)

The approach I posted, multiplying by a prime number before summing, is  

Your approach will throw exceptions in many cases. You need surround
your summation statement with an

unchecked {
// perform sum here
}

[...]

Let me know if you need any more assistance.


Regards,

Sphere 10 Software (www.sphere10.com)
File Server Migrator - Copy Local Users and Groups
http://www.sphere10.com/fileservermigrator/
 

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

Similar Threads


Top