Вопрос

Мне было интересно, возможно ли следующее.Создайте класс, который принимает анонимный тип (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();

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

Это было полезно?

Решение

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

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

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

Другие советы

Специализация невозможна в 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>()

"Специализация" невозможна в C # так, как это происходит в C ++.В .NET generics универсальный класс или метод <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; }
}

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

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

typeof(T)

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

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

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

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

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

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

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

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

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

myString.GetName();

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

Пример:

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
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top