Перегрузка метода с помощью типов С#

Мне было интересно, возможно ли следующее. Создайте класс, который принимает анонимный тип (строка, int, decimal, customObject и т.д.), А затем перегруженные методы, которые выполняют разные операции на основе типа. Пример

    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    
  }

}

Итак, теперь я могу вызвать метод GetName и потому, что я уже передал тип при инициализации объекта, правильный метод найден и выполнен.

TestClass foo = new TestClass<int>();

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

Возможно ли это, или я просто мечтаю?

Ответ 1

То, что вы пытаетесь сделать, возможно следующим образом:

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

В то время как это возможно, вы как бы побеждаете цель дженериков. Точка дженериков - это когда тип не имеет значения, поэтому я не думаю, что в этом случае подходящими являются дженерики.

Ответ 2

"Специализация" на С# невозможна, как в С++. В генерических файлах .NET общий класс или метод <T> должно быть одинаковым для всех возможных значений T. Это позволяет среде выполнения оптимизировать два разных типа ссылок, например TestClass <string> и TestClass < List <int> , используют один и тот же код машинного языка. (разные типы значений получают отдельный машинный код, но вы все еще не можете специализироваться.)

Я нахожу, что иногда помогает создать общий интерфейс или базовый класс следующим образом:

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

И сделайте специализацию в производных классах:

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; }
}

Ответ 3

В С# специализация невозможна. Самая близкая вещь в С# следующая

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

Компилятор С# предпочтет не общий метод для общего метода. Это означает, что вызов с int paramater будет привязан к перегрузке int по сравнению с общим.

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

Это вас укусит, потому что это не применяется, когда метод вызывается в общем случае. В этом случае он будет привязан к общей перегрузке независимо от типа.

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

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

Ответ 4

Нет, это невозможно. То, что вы пытаетесь сделать, похоже на специализированную специализацию на С++, которая (к сожалению) невозможна в С#.

Вам нужно, если /else или включить

typeof(T)

для вызова специализированных реализаций.

Тем не менее, вы можете ограничить тип T либо классом (ссылочным значением), либо структурой (значением) или подклассом определенного базового слова следующим образом:

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

Ответ 5

Будут ли использоваться методы расширения класса?

Вы можете существенно добавлять методы к классам, которые хотите, а затем вы можете называть их одинаково.

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

называется:

myString.GetName();

Ответ 6

С# не поддерживает такую ​​диспетчеризацию.

и это неправильный способ для перегрузки методов также (Error'TestClass уже определяет член, называемый "GetName" с теми же типами параметров), если все внутри < > не является частью сигнатуры метода.

Ответ 7

Если вам нужно выполнять работу по типу в вашем классе, то ваш класс не является общим. Вероятно, вы должны сделать отдельный класс для каждого типа, который вы хотите обработать. Если есть какая-то функциональность, которая имеет правильную причину для генерации, вы можете поместить ее в общий базовый класс.

Пример:

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
}

Ответ 8

Как упоминалось в BFree, вы можете сделать это с деревом if или, скорее, с оператором switch, но в какой-то момент вы захотите, чтобы вы могли просто писать методы и позволить .NET определять это, особенно если вы выращиваете библиотеку перегрузки с течением времени.

Решение есть отражение, хотя оно довольно дешево по производительности в .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;
}

В основном вы просто рассказываете фреймворку Reflection, чтобы сделать этот оператор switch для вас.