passing a generic type to a generic class

C

Charles Churchill

I apologize if this question has been asked before, but after about
half an hour of searching I haven't been able to find an answer online.

My code is beloiw, with comments pertaining to my question
In short my question is why when I pass a generic type directly to the
formatObject function it works fine, but when I pass it to the
checkText function where it is itself a generic argument, and then it
is passed to formatObject, it is seen as an Object and not as the type
it is? Any help would be greatly appreciated.

Here are the relevant sections of my code (I apologize for any typos as
the actual code is on a remote system that I cannot cut and past from)

public class FormatObject
{
public static string formatObject(Object o)
{
return o.ToString();
}

public static string formatObject<K,V>(KeyValuePair<K,V> kvpo)
{
//do some reflectiony sort of things to build up string
// that looks like (KeyValuePair<Int32, Int32> = {10,10})

message += formatObject(kvpo.Key);
// call formatObject on the key and value
// recursively in case they are of a type handled
// by other formatObject calls
return message
}

// a few other overridden formatObject functions taking different
// types (List<T>, List<List<T>>, etc)

}

public class CheckText
{
public bool void checkText(T t, string matchPattern)
{
string ostr;
ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchPattern) != -1)
}
}

public void Main(string[] args)
{
KeyValuePair<int, int> k = new KeyValuePair<int, int>(10,.10);
string formatted = FormatObject.formatObject(k);
// Calls the appropriate function and returns a string consisting of
// (KeyValuePair<Int32, Int32> = {10,10})

bool doesContain = CheckText.checkText(k,"(KeyValuePair");
// returns false as it calls the Object overriden version
// of formatObject. which returns a string
// consisting of "{10,10}"
}
 
S

Stoitcho Goutsev \(100\)

Charles,

Can you please post compilable sample that demonstrates the problem -
console application is preferable.
 
C

Charles Churchill

Stoitcho said:
Charles,

Can you please post compilable sample that demonstrates the problem -
console application is preferable.

Here you go:

namespace TestProgram
{
public class FormatObject
{
public static string formatObject(Object o)
{
return o.ToString();
}

public static string formatObject<K, V>(KeyValuePair<K, V> kvpo)
{
return "KeyValuePair<" + typeof(K).Name + ","
+ typeof(V).Name + "> = " + kvpo.ToString();
}


}

public class CheckText
{
public static bool checkText<T>(T t, string matchThis)
{
string ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchThis) != -1);
}

}

class Program
{
static void Main(string[] args)
{
KeyValuePair<int, int> k = new
KeyValuePair<int,int>(500,2000);
Console.WriteLine(FormatObject.formatObject(k));
bool found = CheckText.checkText(k, "KeyValuePair");
Console.ReadLine();
}
}
}

Thanks
 
B

Bruce Wood

Charles said:
I apologize if this question has been asked before, but after about
half an hour of searching I haven't been able to find an answer online.

My code is beloiw, with comments pertaining to my question
In short my question is why when I pass a generic type directly to the
formatObject function it works fine, but when I pass it to the
checkText function where it is itself a generic argument, and then it
is passed to formatObject, it is seen as an Object and not as the type
it is? Any help would be greatly appreciated.

Here are the relevant sections of my code (I apologize for any typos as
the actual code is on a remote system that I cannot cut and past from)

public class FormatObject
{
public static string formatObject(Object o)
{
return o.ToString();
}

public static string formatObject<K,V>(KeyValuePair<K,V> kvpo)
{
//do some reflectiony sort of things to build up string
// that looks like (KeyValuePair<Int32, Int32> = {10,10})

message += formatObject(kvpo.Key);
// call formatObject on the key and value
// recursively in case they are of a type handled
// by other formatObject calls
return message
}

// a few other overridden formatObject functions taking different
// types (List<T>, List<List<T>>, etc)

}

public class CheckText
{
public bool void checkText(T t, string matchPattern)
{
string ostr;
ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchPattern) != -1)
}
}

public void Main(string[] args)
{
KeyValuePair<int, int> k = new KeyValuePair<int, int>(10,.10);
string formatted = FormatObject.formatObject(k);
// Calls the appropriate function and returns a string consisting of
// (KeyValuePair<Int32, Int32> = {10,10})

bool doesContain = CheckText.checkText(k,"(KeyValuePair");
// returns false as it calls the Object overriden version
// of formatObject. which returns a string
// consisting of "{10,10}"
}

I'm not using generics yet, but a couple of minutes looking at your
code leads me to believe that your problem has nothing to do with
generics, as such. Rather, it involves the following:

"Which overload of a method should be called is determined at compile
time, not run time. Method overRIDES are resolved at run time; method
overLOADS are resolved at compile time."

So, in your case, the line

ostr = FormatObject.formatObject(t);

will result in a call to FormatObject.formatObject(Object o) unless T
is a subtype of KeyValuePair<K, V>. Even if at run time the variable t
refers to an instance of a class that implements the correct interface
/ inherits from the correct class so that a call to
FormatObject.formatObject<K, V> would be possible, it still won't
happen because the compiler has no way of knowing that that will be the
case.

Take a simpler, non-generic example:

public class Foo : IComparable
{
public int CompareTo(object other)
{
// Native Foo object greater than any other kind of object
return 1;
}

public int CompareTo(Foo otherFoo)
{
... do some stuff to compare Foos ...
}
}

public class Other
{
public static int CompareFoo(Foo aFoo, object something)
{
return aFoo.CompareTo(something);
}
}

A call such as myOther.CompareToFoo(...) will _always_ return -1. Why?
Because no matter what is passed as the argument "something", the
compiler has already determined that the correct signature to call is
Foo.CompareTo(object), based on the fact that the "something" argument
is declared as an object type. Even if you pass a Foo instance at run
time, it still calls the Foo.CompareTo(object) method.

The usual way around this is to do the testing yourself, at run time,
to determine whether the more specific method is appropriate:

public int CompareTo(object other)
{
Foo otherFoo = other as Foo;
if (otherFoo != null)
{
return this.CompareTo(otherFoo);
}
else
{
// Native Foo object greater than any other kind of object
return 1;
}
}

.... or something like that.

In your case, you need to test, within formatObject(object o) whether
"o" is an instance of something that could be passed to the generic
method, and then call the generic method if appropriate.

Maybe. As I said, I'm not on V2.0 yet. :)
 
S

Stoitcho Goutsev \(100\)

Charles,

Here is the problem - when you call the method
public static bool checkText<T>(T t, string matchThis)
inside the method the only thing that the compiler knows about the type T is
that it has derived from Object. That's why it will treat the *t* as an
object of type System.Object. It won't even consider the generic
formatObject overload since it cannot infer the types of V and K from the
method's type-argument list. You can easily check this by commenting out the
non-generic formatObject. The compiler will report an error.

Normally one gives hints to the compiler constraining types in the
type-argument list.
Unfortunately in your case I don't think the problem can be solved using
constraints.

My solution to the problem is to use one special overload of the checkTest
method that will provide the compiler with the information it lacks at the
moment.

public static bool checkText<K, V>(KeyValuePair<K, V> t, string matchThis)
{
string ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchThis) != -1);
}

Using this overload the compiler will be able to infer correctly K and V,
thus call generic overload of formatObject.

I also don't see the point of using generic method for the other overload of
that method. It is equivalent of having Object as the parameter type.

Here is your sample reworked:

namespace TestProgram
{
public class FormatObject
{
public static string formatObject(Object o)
{
return o.ToString();
}

public static string formatObject<K, V>(KeyValuePair<K, V> kvpo)
{
return "KeyValuePair<" + typeof(K).Name + ","
+ typeof(V).Name + "> = " + kvpo.ToString();
}


}

public class CheckText
{
public static bool checkText(object t, string matchThis)
{
string ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchThis) != -1);
}

public static bool checkText<K, V>(KeyValuePair<K, V> t, string
matchThis)
{
string ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchThis) != -1);
}

}

class Program
{
static void Main(string[] args)
{
KeyValuePair<int, int> k = new
KeyValuePair<int, int>(500, 2000);
Console.WriteLine(FormatObject.formatObject("100"));
Console.WriteLine(FormatObject.formatObject(k));
bool found = CheckText.checkText("KeyValuePair100", "KeyValuePair");
found = CheckText.checkText(k, "KeyValuePair");
Console.ReadLine();
}
}
}


--
HTH
Stoitcho Goutsev (100)

Charles Churchill said:
Stoitcho said:
Charles,

Can you please post compilable sample that demonstrates the problem -
console application is preferable.

Here you go:

namespace TestProgram
{
public class FormatObject
{
public static string formatObject(Object o)
{
return o.ToString();
}

public static string formatObject<K, V>(KeyValuePair<K, V> kvpo)
{
return "KeyValuePair<" + typeof(K).Name + ","
+ typeof(V).Name + "> = " + kvpo.ToString();
}


}

public class CheckText
{
public static bool checkText<T>(T t, string matchThis)
{
string ostr = FormatObject.formatObject(t);
return (ostr.IndexOf(matchThis) != -1);
}

}

class Program
{
static void Main(string[] args)
{
KeyValuePair<int, int> k = new
KeyValuePair<int,int>(500,2000);
Console.WriteLine(FormatObject.formatObject(k));
bool found = CheckText.checkText(k, "KeyValuePair");
Console.ReadLine();
}
}
}

Thanks
 

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