C # Общий и метод - PullRequest
       89

C # Общий и метод

0 голосов
/ 18 ноября 2008

Как выбрать хороший метод (в приведенном ниже примере показано 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);

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

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

Ответы [ 7 ]

4 голосов
/ 18 ноября 2008

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

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 для достижения этого. Спасибо тебе. Этот ответ не будет принят.

4 голосов
/ 18 ноября 2008

Нет, вы не можете этого сделать. Обобщения не работают как шаблоны 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
    }
}

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

2 голосов
/ 18 ноября 2008

Класс 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());

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

2 голосов
/ 18 ноября 2008

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

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

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

1 голос
/ 18 ноября 2008

Я знаю, что вы обеспокоены боксом / распаковкой, поэтому здесь могут быть задействованы 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.

0 голосов
/ 19 ноября 2008

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

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

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

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

Вы могли бы получить более динамичное поведение, если бы использовали 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; 
        } 

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

0 голосов
/ 18 ноября 2008

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

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

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

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

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

...