C# Generic и метод

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

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

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

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

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

У Object нет упаковки и распаковки - это не тип значения. Я бы просто удалил дженерики или перегрузил метод

Cory Foy 18.11.2008 23:21

Объект к реальному типу ... после преобразования реального типа в объект ...

Patrick Desjardins 18.11.2008 23:29

В любом случае бокса нет. Здесь нет типов значений, поэтому нет бокса.

Jon Skeet 18.11.2008 23:33

Это реальный объект ---> Парсер, который манипулирует данными в типе объекта ----> Возвращает реальный объект. Я вижу некоторое преобразование от реального объекта к объекту, чем от объекта к реальному объекту процесса (код - это небольшой фрагмент для этого вопроса). Так что да, там есть бокс / распаковка.

Patrick Desjardins 18.11.2008 23:38
csharphelp.com/archives/archive100.html какой-то справочник по боксу / распаковке.
Patrick Desjardins 18.11.2008 23:39

У вас должен быть тип значения, чтобы иметь бокс. У вас есть тип значения (то, что объявлено ключевым словом struct)?

Amy B 19.11.2008 00:28

нет структуры, только класс. Неважно, что я хочу, так это не выполнять if (xxx IS yyyy) ... и просто перенаправлять на хороший метод.

Patrick Desjardins 19.11.2008 00:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
1 082
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

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

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

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

Patrick Desjardins 18.11.2008 23:26

Рассмотрите возможность использования сопоставления типа и вызываемого делегата. См. stackoverflow.com/questions/298976/…

Jon Skeet 18.11.2008 23:32

это больше то, что я ищу, я думаю, как настроить карту? Словарь с <Тип, делегат>?

Patrick Desjardins 18.11.2008 23:45

Dictionary <Type, Action>, чтобы вы могли просто вызвать действие.

Jon Skeet 19.11.2008 00:05

Действие не возвращает значение, и мой метод должен возвращать значение. А как насчет того, чтобы создать новый пост с решением? Вот моя попытка: Dictionary <Type, delMapper> map = new Dictionary <Type, delMapper> (); общедоступный Parser () {map.Add (typeof (ObjectType1), новый delMapper (действие)); }

Patrick Desjardins 19.11.2008 00:23

ОН - это то, о чем вы говорите: dotnetperls.com/Content/Action-Dictionary.aspx, но у него нет параметра или возвращаемого значения. Но спасибо за информацию, я буду работать над этим.

Patrick Desjardins 19.11.2008 01:08

Если вам нужно вернуть значение, сделайте его Func. Я использовал ваш пример кода.

Jon Skeet 19.11.2008 01:15

Так что добавьте - это просто делегат. См. csharpindepth.com/Articles/Chapter1/Versions.aspx для кода, который вы можете вырезать и вставить. (Обратите внимание, что это .NET 2.0, в котором его нет - сам C# не определяет никаких типов делегатов.)

Jon Skeet 19.11.2008 01:45

Это не работает по той причине, что при создании словаря вам нужно указать тип, так как у меня много объектов, я не могу этого сделать: Dictionary <Type, Func <??? >> map = new Dictionary <Тип, Func <??? >> ();

Patrick Desjardins 19.11.2008 03:13

Ах, вы не сказали, что ему нужно вернуть экземпляр T. В этом случае вам нужно просто сделать его Func <object> и привести его в Execute.

Jon Skeet 19.11.2008 09:18

хорошо, спасибо за все, если я это сделаю, я сохраню свой большой IF / Else IF.

Patrick Desjardins 19.11.2008 16:02

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

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

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

Ненужно, "obj" уже имеет тип T, вам не нужно приводить его.

Lasse V. Karlsen 18.11.2008 23:33

Вторая сработает, если «as» следует за реальным типом, а не за буквой T (ой, я не очень хорошо прочитал решение, как мне кажется). Так что это работает, но мне все еще нужно делать много IF ...

Patrick Desjardins 19.11.2008 00:05

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

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

Да, но я не могу сделать для этого 100 классов ... Я должен сказать вам, что у нас здесь много методов, потому что это построитель объектов базы данных. Да, я подумал, но я хотел бы, чтобы все объекты базы данных строились в одном классе.

Patrick Desjardins 18.11.2008 23:30

И я бы не стал менять всю BusinessLogic на IAction ... этот вариант действительно не подходит для ситуации.

Patrick Desjardins 18.11.2008 23:42

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

workmad3 19.11.2008 01:14

Класс требует выполнять свою работу, а не загружать его или другую задачу (таким образом, если мы изменим способ загрузки или другой метод, это не повлияет на бизнес-объект. Я не верю в этом контексте, что интерфейс требуется, НО спасибо много (Ваш код отлично подходит для других целей)

Patrick Desjardins 19.11.2008 01:22

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

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

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

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

Благодарность joshua.ewer за поиск страницы руководства

Если кто-то ищет информацию в Google, это называется ограничениями. Вы можете ограничивать на основе типа, наследования или реализации и т. д. msdn.microsoft.com/en-us/library/d5x73970.aspx

joshua.ewer 18.11.2008 23:47

The class Parser has a lot of private method that are called by the execute method depending of the object type. It needs to redirect to the good method.

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

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());

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

Хороший ответ, и я проголосую за вас, НО проблема в том, что метод на самом деле исходит из интерфейса, и я не мог не изменить этот интерфейс, чтобы перегрузить его для всех типов.

Patrick Desjardins 18.11.2008 23:55

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

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

x равно 1, а не 2.

Код такой, потому что когда-нибудь мы получаем данные из кеша (десериализуем), и у нас есть новая ссылка, поэтому нам нужно вернуться. Нам нужно передать объект по параметру, чтобы знать, какой объект загружать ... вы его понимаете?

Patrick Desjardins 19.11.2008 00:03

Для меня не имеет значения, поступают ли данные из кеша или из Монголии, у вас проблема с передачей по значению, если какой-либо из типов является типами значений.

Amy B 19.11.2008 00:24

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

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

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

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

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

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

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