Generic methods..

R

Ram

Hi All,

I am new to this Generics. I started with a Generic method which
doesn't return any value. It worked well.

Then I tried to write a generic method which will sum up the given
values and return the result. This is the piece of code.

private T Add<T>(T a, T b)
{

T c = a + b;


return c;
}



While compilation, I am getting the following error.

" Operator '+' cannot be applied to operands of type 'T' and 'T' ".

What is the problem?. How to perform operations on the Generic
variables?

How to write generic methods that will return a value?

Please throw some light on this.

Thanks in advance.


Regards,
Ram
 
M

Marc Gravell

A common question. Generics are not like C++ templates, and the short
answer is that you can't use operator overloads (such as +, -, etc)
with gnerics. You can use other constraints, however, such as
"implements <x> interface", "is reference-type", "has a default
colnstructor". They are very useful, but not directly in your scenario
I'm afraid.

Marc
 
L

Laura T.

True. There are some things that can be done but not the C++ way at all.

For example your Add *could* check common types and behave as the best.
The only thing is that if someone calls the function with a type you have
not implemented (say Add<Form>), it will not work.

public class Template
{
public T Add<T>(T a1, T a2) where T:class
{
if (a1 is string && a2 is string)
{
return string.Concat(a1, a2) as T;
}
else if (a1 is int && a2 is int)
{
return (Convert.ToInt32(a1) + Convert.ToInt32(a1)) as T;
}

return default(T);
}
}

static void Main(string[] args)
{
Template x = new Template();
int v = (int)x.Add<object>(2, 3);
}
 
M

Marc Gravell

This example would be better handled with overloading. The whole point
of generics is that you *expect* it to be called for typed you don't
know about; also - unless I am very mistaken it can't work with int
because of the T : class

public int Add(int a1, int a2) {return a1 + a2;}
public string Add(string a1, string a2) {return a1 + a2;} // Concat by
the compiler
etc

Marc
 
M

Marc Gravell

A true generic implementation must support types that don't exist in
the (base) library wrapping the code. Maybe I am overcomplicating
things, but you could do this via interfaces etc as follows. You could
add a few more "standard" implementations, but more importantly the
end-developer can hook their own definitions for complex, quoternian,
SomeClass, SomeStruct, etc...

Anyways, here it is: (this is just a demo... not production code)

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


static class Program {
static void Main() {
Console.WriteLine(Foo(1, 4, 6));
Console.WriteLine(Foo(12.0F, 1.2F, 6F));
Console.WriteLine(Math.Add("abc","def", "ghi"));
try {
Console.WriteLine(Foo("abc", "def", "ghi"));
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
static T Foo<T>(T arg1, T arg2, T arg3) {
return (Math.Product(Math.Add(arg1, arg2), arg3));
}

}
public interface IMath<T> {
T Add(T lhs, T rhs);
T Add(params T[] args);
T Product(T lhs, T rhs);
T Product(params T[] args);
T Negate(T arg);
T Invert(T arg);
T Unit { get;}
T Zero { get;}
// etc
}
public abstract class MathBase<T> : IMath<T> {
protected T ThrowNotSupported(string name) {
throw new NotSupportedException(name + " is not supported for
" + typeof(T).FullName);
}
virtual public T Add(params T[] args) {
if (args == null) throw new ArgumentNullException("args");
IMath<T> provider = Math<T>.Default;
switch(args.Length) {
case 0: return provider.Zero;
case 1: return args[0];
case 2: return provider.Add(args[0], args[1]);
}

T value = provider.Add(args[0], args[1]);
for (int i = 2; i < args.Length; i++) {
value = provider.Add(value, args);
}
return value;
}
virtual public T Product(params T[] args) {
if (args == null) throw new ArgumentNullException("args");
IMath<T> provider = Math<T>.Default;
switch(args.Length) {
case 0: return provider.Unit;
case 1: return args[0];
case 2: return provider.Product(args[0], args[1]);
}

T value = provider.Product(args[0], args[1]);
for(int i = 2; i < args.Length ; i++) {
value = provider.Product(value, args);
}
return value;
}
virtual public T Add(T lhs, T rhs) {
return ThrowNotSupported("Add");
}
virtual public T Product(T lhs, T rhs) {
return ThrowNotSupported("Product");
}
virtual public T Negate(T arg) {
return ThrowNotSupported("Negate");
}
virtual public T Invert(T arg) {
return ThrowNotSupported("Invert");
}
virtual public T Unit {
get { return ThrowNotSupported("Unit");}
}
virtual public T Zero { // this is actually a reasonable default
implementation
get { return default(T); }
}
}
internal static class Math<T> { // avoid using the dictionary on each
access
private static readonly IMath<T> provider;
public static IMath<T> Default { get { return provider; } }
static Math() {
provider = Math.GetHandler<T>();
}

}
public static class Math {
public static T Add<T>(T lhs, T rhs) {
return Math<T>.Default.Add(lhs, rhs);
}
public static T Add<T>(params T[] args) {
return Math<T>.Default.Add(args);
}
public static T Product<T>(T lhs, T rhs) {
return Math<T>.Default.Product(lhs, rhs);
}
public static T Product<T>(params T[] args) {
return Math<T>.Default.Product(args);
}
public static T Negate<T>(T arg) {
return Math<T>.Default.Negate(arg);
}
public static T Invert<T>(T arg) {
return Math<T>.Default.Invert(arg);
}
public static T Unit<T>() {
return Math<T>.Default.Unit;
}
public static T Zero<T>() {
return Math<T>.Default.Zero;
}

static Math() {
providers = new Dictionary<Type, object>();
AddProvider<int>(new MathInt32Provider());
AddProvider<float>(new MathSingleProvider());
AddProvider<string>(new MathStringProvider());
}
internal sealed class MathInt32Provider : MathBase<int> {
override public int Add(int lhs, int rhs) {return lhs + rhs;}
override public int Product(int lhs, int rhs) { return lhs *
rhs; }
override public int Negate(int arg) { return -arg; }
override public int Invert(int arg) { throw new
NotSupportedException(); }
override public int Unit { get { return 1; } }
}
internal sealed class MathSingleProvider : MathBase<float> {
override public float Add(float lhs, float rhs) { return lhs +
rhs; }
override public float Product(float lhs, float rhs) { return
lhs * rhs; }
override public float Negate(float arg) { return -arg; }
override public float Invert(float arg) { return 1.0F / arg; }
override public float Unit { get { return 1.0F; } }
}
internal sealed class MathStringProvider : MathBase<string> {
override public string Add(string lhs, string rhs) { return
lhs + rhs; }
override public string Zero { get { return ""; } }
public override string Add(params string[] args) {
if (args == null) throw new ArgumentNullException("args");
switch (args.Length) {
case 0: return "";
case 1: return args[0];
case 2: return string.Concat(args[0], args[1]);
case 3: return string.Concat(args[0], args[1],
args[2]);
case 4: return string.Concat(args[0], args[1],
args[2], args[3]);
}
StringBuilder sb = new StringBuilder();
foreach (string arg in args) {
sb.Append(arg);
}
return sb.ToString();
}
}
static readonly Dictionary<Type, object> providers;
public static void AddProvider<T>(IMath<T> provider) {
if(provider==null) throw new
ArgumentNullException("provider");
lock (provider) {
providers.Add(typeof(T), provider);
}
}
internal static IMath<T> GetHandler<T>() {
lock (providers) {
object provider;
if (!providers.TryGetValue(typeof(T), out provider)) {
throw new NotSupportedException("No IMath<T> provider
exists for " + typeof(T).FullName);
}
return (IMath<T>)provider; // cast is OK since it made it
in
}
}
}
 

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