Как выбрать хороший метод (в приведенном ниже примере показано 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);
это работает ... почему?
И ... Я не могу перегрузить для всех типов метод выполнения, потому что этот метод взят из интерфейса. Вот почему все необходимо вызывать из этого метода.
Объект к реальному типу ... после преобразования реального типа в объект ...
В любом случае бокса нет. Здесь нет типов значений, поэтому нет бокса.
Это реальный объект ---> Парсер, который манипулирует данными в типе объекта ----> Возвращает реальный объект. Я вижу некоторое преобразование от реального объекта к объекту, чем от объекта к реальному объекту процесса (код - это небольшой фрагмент для этого вопроса). Так что да, там есть бокс / распаковка.
У вас должен быть тип значения, чтобы иметь бокс. У вас есть тип значения (то, что объявлено ключевым словом struct)?
нет структуры, только класс. Неважно, что я хочу, так это не выполнять if (xxx IS yyyy) ... и просто перенаправлять на хороший метод.





Нет, ты не можешь этого сделать. Универсальные шаблоны работают не так, как шаблоны 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 в зависимости от типа объекта. Необходимо перенаправить на хороший метод.
Рассмотрите возможность использования сопоставления типа и вызываемого делегата. См. stackoverflow.com/questions/298976/…
это больше то, что я ищу, я думаю, как настроить карту? Словарь с <Тип, делегат>?
Dictionary <Type, Action>, чтобы вы могли просто вызвать действие.
Действие не возвращает значение, и мой метод должен возвращать значение. А как насчет того, чтобы создать новый пост с решением? Вот моя попытка: Dictionary <Type, delMapper> map = new Dictionary <Type, delMapper> (); общедоступный Parser () {map.Add (typeof (ObjectType1), новый delMapper (действие)); }
ОН - это то, о чем вы говорите: dotnetperls.com/Content/Action-Dictionary.aspx, но у него нет параметра или возвращаемого значения. Но спасибо за информацию, я буду работать над этим.
Если вам нужно вернуть значение, сделайте его Func. Я использовал ваш пример кода.
Так что добавьте - это просто делегат. См. csharpindepth.com/Articles/Chapter1/Versions.aspx для кода, который вы можете вырезать и вставить. (Обратите внимание, что это .NET 2.0, в котором его нет - сам C# не определяет никаких типов делегатов.)
Это не работает по той причине, что при создании словаря вам нужно указать тип, так как у меня много объектов, я не могу этого сделать: Dictionary <Type, Func <??? >> map = new Dictionary <Тип, Func <??? >> ();
Ах, вы не сказали, что ему нужно вернуть экземпляр T. В этом случае вам нужно просто сделать его Func <object> и привести его в Execute.
хорошо, спасибо за все, если я это сделаю, я сохраню свой большой IF / Else IF.
Я не пробовал, но можно ли это сделать?
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, вам не нужно приводить его.
Вторая сработает, если «as» следует за реальным типом, а не за буквой T (ой, я не очень хорошо прочитал решение, как мне кажется). Так что это работает, но мне все еще нужно делать много IF ...
Вы рассматривали интерфейсы?
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;
}
}
Это решение потребует изменения всего объекта бизнес-логики, чтобы иметь этот интерфейс. Это действительно не то, что нужно делать (в моей ситуации). А в другой ситуации я всегда предпочитаю иметь чистый BusinessObject, у которого нет интерфейса, не связанного с бизнесом. В моем вопросе мне нужно решение, которое больше связано с методом Generic / Object / Delegate для его достижения. Спасибо. Этот ответ не будет принят.
Да, но я не могу сделать для этого 100 классов ... Я должен сказать вам, что у нас здесь много методов, потому что это построитель объектов базы данных. Да, я подумал, но я хотел бы, чтобы все объекты базы данных строились в одном классе.
И я бы не стал менять всю BusinessLogic на IAction ... этот вариант действительно не подходит для ситуации.
Достаточно справедливо ... это звучит как работа для интерфейса. Если единственная причина, по которой вы не рассматриваете интерфейсы, заключается в том, что у вас есть много методов и классов, которые нужно изменить, вы можете написать сценарий, чтобы автоматизировать этот процесс для вас :)
Класс требует выполнять свою работу, а не загружать его или другую задачу (таким образом, если мы изменим способ загрузки или другой метод, это не повлияет на бизнес-объект. Я не верю в этом контексте, что интерфейс требуется, НО спасибо много (Ваш код отлично подходит для других целей)
IIRC, вы можете использовать предложение "where", чтобы разрешить это
public T execute<T>(T obj) where : /* somthing */
{
}
Мне всегда приходится искать его в Google, так что я оставлю все как есть.
редактировать: читать некоторые комментарии. Я бы не советовал вызывать код конкретного типа. Скорее поместите этот код в виртуальную функцию и вызовите ее. Подпись вызова может быть длинной, но для этого и нужно автоматическое завершение.
Благодарность joshua.ewer за поиск страницы руководства
Если кто-то ищет информацию в Google, это называется ограничениями. Вы можете ограничивать на основе типа, наследования или реализации и т. д. msdn.microsoft.com/en-us/library/d5x73970.aspx
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());
Нет упаковки / распаковки, и вызывается соответствующий метод.
Хороший ответ, и я проголосую за вас, НО проблема в том, что метод на самом деле исходит из интерфейса, и я не мог не изменить этот интерфейс, чтобы перегрузить его для всех типов.
Я знаю, что вас беспокоит упаковка / распаковка, поэтому здесь могут быть задействованы типы значений.
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.
Код такой, потому что когда-нибудь мы получаем данные из кеша (десериализуем), и у нас есть новая ссылка, поэтому нам нужно вернуться. Нам нужно передать объект по параметру, чтобы знать, какой объект загружать ... вы его понимаете?
Для меня не имеет значения, поступают ли данные из кеша или из Монголии, у вас проблема с передачей по значению, если какой-либо из типов является типами значений.
Дженерики происходят во время компиляции. Его лучше всего использовать, когда вы хотите, чтобы один и тот же код применялся к разным типам. Он не динамический, поэтому не поможет вам переключаться между методами в зависимости от типов ввода.
Разрешение перегрузки, как в ответе Дэвида Б, работает, но также происходит во время компиляции.
Код в вашем обновлении делает то же самое. Он выполняет приведение (после тщательной проверки типов), а затем использует перегрузку для разрешения метода.
Я чувствую, что вы хотите переключать методы в зависимости от ввода во время выполнения.
Вы могли бы получить более динамичное поведение, если бы использовали 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;
}
Возможно, он немного хрупкий, но в примере работает.
У Object нет упаковки и распаковки - это не тип значения. Я бы просто удалил дженерики или перегрузил метод