Перегрузка метода с типами C # - PullRequest
11 голосов
/ 22 октября 2009

Мне было интересно, возможно ли следующее. Создайте класс, который принимает анонимный тип (string, 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's the only one with a "int" type
foo.GetName();

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

Ответы [ 8 ]

12 голосов
/ 22 октября 2009

То, что вы пытаетесь сделать, возможно так:

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

Хотя это возможно , вы как бы побеждаете цель обобщений. Суть обобщений заключается в том, что тип не имеет значения , поэтому я не думаю, что обобщение в этом случае подходит

4 голосов
/ 22 октября 2009

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

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

Компилятор C # предпочтет неуниверсальный метод по сравнению с универсальным методом. Это означает, что вызов с параметром int будет связан с перегрузкой 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 голосов
/ 22 октября 2009

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

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

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; }
}
1 голос
/ 22 октября 2009

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

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

typeof(T)

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

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

 public Foo<T> DoBar<T>() where T : FooBase;
0 голосов
/ 25 июня 2015

Как упомянул 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, чтобы вы делали это переключение за вас.

0 голосов
/ 22 октября 2009

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

Пример:

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
}
0 голосов
/ 22 октября 2009

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

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

0 голосов
/ 22 октября 2009

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

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

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

вызывается с помощью:

myString.GetName();
...