Вопрос

Как мне выбрать хороший метод (в примере ниже показаны 2 разных способа, которые не работают).Для выполнения этой работы я использовал переменную типа Object с IF и IS, но стараюсь избегать использования Object и упаковки/распаковки.Я подумал, что Дженерик справится с этой задачей, но я застрял здесь.

Вот небольшой фрагмент кода, иллюстрирующий мой вопрос:

class Program
{
    static void Main(string[] args)
    {
        Parser p = new Parser();
        ObjectType1 o1 = new ObjectType1();
        p.execute(o1);
        Console.Read();
    }
}

class Parser
{
    public T execute<T>(T obj)
    {
        /*
        if (obj is ObjectType1)
            this.action((ObjectType1)obj);
        else if (obj is ObjectType2)
            this.action((ObjectType2)obj);
        */
        this.action(obj);
        return obj;
    }

    private void action(ObjectType1 objectType1)
    {
        Console.WriteLine("1");
    }

    private void action(ObjectType2 objectType2)
    {
        Console.WriteLine("2");
    }
}


class ObjectType1
{
}

class ObjectType2
{
}

Обновлять

Мне не нужен интерфейс и класс.Извини.Я знал, что это не цель вопроса.

Приведение с помощью (ObjectType)obj не работает, но если вы это сделаете:

        if (obj is ObjectType1)
            this.action(obj as ObjectType1);
        else if (obj is ObjectType2)
            this.action(obj as ObjectType1);

оно работает...почему?

И...Я не могу перегрузить метод выполнения для всех типов, поскольку этот метод взят из интерфейса.Вот почему все нужно вызывать из этого метода.

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

Решение

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

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

using System;

class Test
{    
    static void Main()
    {
        string x = "hello";
        string y = string.Copy(x);

        Console.WriteLine(x==y); // Overload used
        Compare(x, y);
    }

    static void Compare<T>(T x, T y) where T : class
    {
        Console.WriteLine(x == y); // Reference comparison
    }
}

Трудно сказать, как лучше поступить, не зная больше о том, что вы хотите сделать.

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

Рассматривали ли вы интерфейсы?

interface IAction
{
   void action();
}

class ObjectType1 : IAction
{
   void action() {
      Console.WriteLine("1");
   }
}

class ObjectType2 : IAction
{
    void action() {
      Console.WriteLine("2");
    }
}

class Parser
{
   public IAction execute(IAction obj)
   {
      obj.action();
      return obj;
   }
}

Отредактировано OP:

Для этого решения потребуется изменить все объекты бизнес-логики, чтобы иметь этот интерфейс. Это действительно не то, что нужно делать (в моей ситуации). И, в другой ситуации, я всегда предпочитаю иметь чистый BusinessObject, у которого нет интерфейса, не связанного с бизнесом. В моем вопросе я хочу решение, которое больше связано с методом Generic / Object / Delegate для достижения этого. Спасибо тебе. Этот ответ не будет принят.

  

Класс Parser имеет множество закрытых методов, которые вызываются методом execute в зависимости от типа объекта. Нужно перенаправить на хороший метод.

Компилятор сделает эту работу за вас. Просто используйте перегрузки.

class Parser
{
    public ObjectType1 action(ObjectType1 objectType1)
    {
        Console.WriteLine("1");
        return objectType1;
    }
    public ObjectType2 action(ObjectType2 objectType2)
    {
        Console.WriteLine("2");
        return objectType2;
    }
}

class ObjectType1 { }
struct ObjectType2 { }

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

Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());

Бокса / распаковки нет, и вызывается соответствующий метод.

Я не пробовал, но сможешь ли ты это сделать?

public T execute<T>(T obj)
{
    this.action((T)obj);
    return obj;
}

(судя по комментариям, не работает)

или

public T execute<T>(T obj)
{
    this.action(obj as T);
    return obj;
}

(судя по комментариям, работает)

Я знаю, что вы обеспокоены боксом / распаковкой, поэтому здесь могут быть задействованы ValueTypes.

public T execute<T>(T obj)   
{        
    this.action(obj);
    return obj;
}

Предположим, что действие модифицирует объект obj, а также предположим, что это изменение важно для вызывающей стороны (именно поэтому вы возвращаете значение обратно вызывающей стороне). Этот код имеет неприятный дефект передачи по значению.

Рассмотрим этот код:

    public int execute(int obj)   
    {        
        this.action(obj);
        return obj;
    }

    public void action(int obj)
    {
        obj = obj + 1;
    }

Вызывается таким образом.

int x = p.execute(1);

х равен 1, а не 2.

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

Разрешение перегрузки, как в ответе Дэвида Б., работает, но также происходит во время компиляции.

Код в вашем обновлении делает то же самое. Он преобразуется (после тщательной проверки типов), а затем использует перегрузку для разрешения метода.

Я чувствую, что вы хотите переключать методы на основе ввода во время выполнения.

Вы могли бы получить более динамичное поведение, если бы использовали Reflection.

        public object execute(object obj) 
        {
            MethodInfo m = typeof(Parser).GetMethod(
                "action", 
                BindingFlags.Instance | BindingFlags.NonPublic, 
                null, 
                new Type[] { obj.GetType() }, 
                null);
            m.Invoke(this, new object[] { obj });
            return obj; 
        } 

Возможно, это немного хрупко, но это работает в примере.

IIRC вы можете использовать " где " пункт, чтобы разрешить это

public T execute<T>(T obj) where : /* somthing */
{
}

Я всегда должен гуглить это, так что я оставлю это в покое.

редактировать: читать некоторые комментарии. Я бы не советовал называть тип конкретным кодом. Скорее поместите этот код в виртуальную функцию и вызовите это. Подпись вызова может быть длинной, но для этого нужно автоматическое завершение.

Koodos to joshua.ewer для поиска страницы руководства

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