开发者

Method Overloading with Types C#

开发者 https://www.devze.com 2022-12-09 08:41 出处:网络
I was wondering if the following is possible. Create a class that accepts an anonymous type (string, int, decimal, customObject, etc), then have overloaded methods that do different operations based o

I was wondering if the following is possible. Create a class that accepts an anonymous type (string, int, decimal, customObject, etc), then have overloaded methods that do different operations based on the Type. Example

    class TestClass<T>
{
  public void GetName<string>()
  {
      //do work knowing that the type is a string    
  }

  public string GetName<int>()
  {
      //do work knowing that the type is an int

  } 

  public string GetName<int>(int addNumber)
  {
      //do work knowing that the type is an int (overloaded)    
  } 

  public string GetName<DateTime>()
  {
      //do work knowing that the type is a DateTime

  } 

  public string GetName<customObject>()
  {
      //do work knowing that the type is a customObject type    
  }

}

So now 开发者_开发问答I could call the GetName method, and because I already passed in the type when I initialized the object, the correct method is found and executed.

TestClass foo = new TestClass<int>();

//executes the second method because that's the only one with a "int" type
foo.GetName();

Is this possible or am I just dreaming?


What you're trying to do is possible like this:

class TestClass<T>
{
   public string GetName<T>()
   {
      Type typeOfT = typeof(T);
      if(typeOfT == typeof(string))
      {
          //do string stuff
      }
   }
}

While this is possible, you're kind of defeating the purpose of generics. The point of generics is when the type doesn't matter, so I don't think generics is appropriate in this case.


"Specialization" is not possible in C# the way it is in C++. In .NET generics, a generic class or method of <T> must be the same for all possible values of T. This allows the runtime to do an optimization that two different reference types, say TestClass<string> and TestClass<List<int>>, share the same machine language code. (different value types get separate machine code, but you still can't specialize.)

I find it sometimes helps to create a generic interface or base class like this:

abstract class Base<T> {
  public abstract T GetName();
  // any common code goes here that does not require specialization
}

And do specialization in derived classes:

class IntVersion : Base<int> {
  public override int GetName() { return 1; }
  public int GetName(int addNumber) { ... }
}
class StringVersion : Base<string> {
  public override string GetName() { return "foo"; }
}
class DateTimeVersion : Base<DateTime> {
  public override DateTime GetName() { return DateTime.Now; }
}


Specialization is not possible in C#. The closest thing in C# is the following

public void Example() {
  public static void Method<T>(T value) { ... }
  public static void Method(int value){ ... }
  public static void Method(string) { ... }
}

The C# compiler will prefer a non-generic method over a generic method. This means that calling with an int paramater will bind to the int overload vs. the generic one.

Example.Method(42);  // Method(int)
Example.Method(new Class1())  // Method<T>(T)

This will bite you though because this does not apply when the method is called generically. In that case it will bind to the generic overload no matter the type.

public void Gotcha<T>(T value) {
  Example.Method(value);
}

Gotcha(42);  // Still calls Example.Method<T>()


No, this is not possible. What you are trying to do is similar to template specialization in C++, which is (sadly) not possible in C#.

You need to if/else or switch on

typeof(T)

to invoke specialized implementations.

However, you can constraint the type of T to be either a class (reference Value) or struct (value) or subclass of a certain baseclass like so:

 public Foo<T> DoBar<T>() where T : FooBase;


c# doesn't have support for such dispatching.

and this is not right way for doing method overloading also (Error'TestClass' already defines a member called 'GetName' with the same parameter types) as long as all inside <> is not a part of method signature.


Would using class extension methods work for you?

You can essentially add methods to the classes you want, and then you can call it the same way.

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int GetName(this String str)
        {
            ...
        }
    }   
}

called using:

myString.GetName();


If you need to do type-specific work in your class, then your class is not generic. You should probably make a seperate class for each type you want to handle. If there is some functionality that does have a proper reason for being generic, you can put it in an common base class.

An example:

abstract class TestClass<T>
{
    public List<T> Items { get; set; }

    // other generic (i.e. non type-specific) code
}

class IntTestClass : TestClass<int>
{
    public string GetName()
    {
        // do work knowing that the type is an int
    }

    // other code specific to the int case
}

class StringTestClass : TestClass<string>
{
    public string GetName()
    {
        // do work knowing that the type is a string
    }

    // other code specific to the string case
}


As BFree mentioned you can do this with an if tree, or more likely a switch statement, but at some point you'll wish you could just write methods and let .Net figure it out, especially if you grow the library of overloads over time.

The solution there is reflection, although it is pretty cheap performance-wise in .Net:

using System.Reflection;
...

public string DoSomething(object val)
{
    // Force the concrete type
    var typeArgs = new Type[] { val.GetType() };

    // Avoid hard-coding the overloaded method name
    string methodName = new Func<string, string>(GetName).Method.Name;

    // Use BindingFlags.NonPublic instead of Public, if protected or private
    var bindingFlags = BindingFlags.Public | BindingFlags.Instance;

    var method = this.GetType().GetMethod(
        methodName, bindingFlags, null, typeArgs, null);

    string s = (string)method.Invoke(this, new object[] { val });

    return s;
}

You're basically just telling the Reflection framework to go do that switch statement for you.

0

精彩评论

暂无评论...
验证码 换一张
取 消