Возникла следующая проблема. Есть словарь, где ключ — это идентификатор модели, а значение — объект, являющийся наследником от базового абстрактного класса AbstractModel. (Т.е. тип словаря — Dictionary<string, AbstractModel>) Классов-наследников достаточно много. Для примера назовем их ConcreteModel1, ConcreteModel2 и т.д. Задача следующая — по заданному идентификатору (ключу) необходимо привести соответствующий объект (значение) к конкретному типу. Проблема в том, что тип этот я не знаю. Пока что вижу 3 варианта решения:
1. Добавить в AbstractModel поле перечислимого типа, по которому можно было бы определить тип модели (возникают риски при ошибке указания нужного перечисления)
2. Использовать рефлекшн (неэстетично)
3. Использовать говнокод (var cm = abstractModel as ConcreteModel1; if (cm != null) { делаем приведение })
Задача выглядит тривиальной, но я за 10 лет ни с чем подобным не сталкивался. Может быть, есть какие-нибудь варианты элегантного решения?
Re: Проблема с приведением типов
От:
Аноним
Дата:
27.01.12 06:49
Оценка:
Здравствуйте, Злобастик, Вы писали:
З>Коллеги, добрый день!
З>Возникла следующая проблема. Есть словарь, где ключ — это идентификатор модели, а значение — объект, являющийся наследником от базового абстрактного класса AbstractModel. (Т.е. тип словаря — Dictionary<string, AbstractModel>) Классов-наследников достаточно много. Для примера назовем их ConcreteModel1, ConcreteModel2 и т.д. Задача следующая — по заданному идентификатору (ключу) необходимо привести соответствующий объект (значение) к конкретному типу. Проблема в том, что тип этот я не знаю. Пока что вижу 3 варианта решения:
З>1. Добавить в AbstractModel поле перечислимого типа, по которому можно было бы определить тип модели (возникают риски при ошибке указания нужного перечисления) З>2. Использовать рефлекшн (неэстетично) З>3. Использовать говнокод (var cm = abstractModel as ConcreteModel1; if (cm != null) { делаем приведение })
З>Задача выглядит тривиальной, но я за 10 лет ни с чем подобным не сталкивался. Может быть, есть какие-нибудь варианты элегантного решения?
Зачем приводить в итоге ? что дальше?
Например можно использовать interface
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Злобастик, Вы писали:
З>>Коллеги, добрый день!
З>>Возникла следующая проблема. Есть словарь, где ключ — это идентификатор модели, а значение — объект, являющийся наследником от базового абстрактного класса AbstractModel. (Т.е. тип словаря — Dictionary<string, AbstractModel>) Классов-наследников достаточно много. Для примера назовем их ConcreteModel1, ConcreteModel2 и т.д. Задача следующая — по заданному идентификатору (ключу) необходимо привести соответствующий объект (значение) к конкретному типу. Проблема в том, что тип этот я не знаю. Пока что вижу 3 варианта решения:
З>>1. Добавить в AbstractModel поле перечислимого типа, по которому можно было бы определить тип модели (возникают риски при ошибке указания нужного перечисления) З>>2. Использовать рефлекшн (неэстетично) З>>3. Использовать говнокод (var cm = abstractModel as ConcreteModel1; if (cm != null) { делаем приведение })
З>>Задача выглядит тривиальной, но я за 10 лет ни с чем подобным не сталкивался. Может быть, есть какие-нибудь варианты элегантного решения?
А>Зачем приводить в итоге ? что дальше? А>Например можно использовать interface
Конкретный тип нужен для того, чтобы понять, каким алгоритмом его обрабатывать. А интерфейс как поможет? Я что-то в упор не вижу.
Re[3]: Проблема с приведением типов
От:
Аноним
Дата:
27.01.12 07:00
Оценка:
Здравствуйте, Злобастик, Вы писали:
З>Конкретный тип нужен для того, чтобы понять, каким алгоритмом его обрабатывать. А интерфейс как поможет? Я что-то в упор не вижу.
Реализовать алгоритм обработки в самом объекте, почему нет?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Злобастик, Вы писали:
З>>Конкретный тип нужен для того, чтобы понять, каким алгоритмом его обрабатывать. А интерфейс как поможет? Я что-то в упор не вижу.
А>Реализовать алгоритм обработки в самом объекте, почему нет?
Эти объекты должны содержать исключительно данные. (по сути эти объекты — часть модели данных). Алгоритмы должны быть известны контроллеру (под известны я подразумеваю, что либо реализованы в нем, либо делегированы). Этот же контроллер обновляет графическое представление при изменении модели.
Здравствуйте, Злобастик, Вы писали:
З>Алгоритмы должны быть известны контроллеру (под известны я подразумеваю, что либо реализованы в нем, либо делегированы). Этот же контроллер обновляет графическое представление при изменении модели.
Бизнес-логика в контроллере? Это что за шаблон используется
Затем чуствуется в этой логике дыра. Вы изначально используете абстракцию, но потом приводите к конкоретным реализациям.
Ну а по теме — вроде само логичное тут enum. Но я бы все же пересмотрел архитектуру.
Здравствуйте, Злобастик, Вы писали:
З>Здравствуйте, Аноним, Вы писали:
А>>Здравствуйте, Злобастик, Вы писали:
З>>>Конкретный тип нужен для того, чтобы понять, каким алгоритмом его обрабатывать. А интерфейс как поможет? Я что-то в упор не вижу.
А>>Реализовать алгоритм обработки в самом объекте, почему нет?
З>Эти объекты должны содержать исключительно данные. (по сути эти объекты — часть модели данных). Алгоритмы должны быть известны контроллеру (под известны я подразумеваю, что либо реализованы в нем, либо делегированы). Этот же контроллер обновляет графическое представление при изменении модели.
заведите "реестр" алгоритмов(стратегий)
interface IRegistry<T> where T : ModelBase
{
IStrategy Get(T model);
}
реализацию постройте на базе словаря Type — IStrategy, где Type конкретный тип модели
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
З>Задача выглядит тривиальной, но я за 10 лет ни с чем подобным не сталкивался. Может быть, есть какие-нибудь варианты элегантного решения?
Если C#4, можно на динамиках:
dynamic d = abstractInstance;
//динамик сам разрулит тип и вызовет нужный метод
ConcreteStrategy(d);
void ConcreteStrategy(ConcreteModel1 cm);
void ConcreteStrategy(ConcreteModel2 cm);
void ConcreteStrategy(ConcreteModel3 cm);
void ConcreteStrategy(AbstractModel am)
{
throw new NotImplementedException("Нет стратегии для модели типа " + am.GetType().Name);
}
Здравствуйте, Doc, Вы писали:
Doc>Здравствуйте, Злобастик, Вы писали:
З>>Алгоритмы должны быть известны контроллеру (под известны я подразумеваю, что либо реализованы в нем, либо делегированы). Этот же контроллер обновляет графическое представление при изменении модели.
Doc>Бизнес-логика в контроллере? Это что за шаблон используется
Нет, вся бизнес-логика находится в стороннем классе. Контроллер осуществляет лишь общее взаимодействие. Например, при добавлении нового элемента в модель мы используем метод контроллера AddElement, который добавляет элемент в модель, а затем обновляет графическое представление через класс-построитель. Модель должна быть макисмально чистой из-за того, что используется в том числе для экспорта и сохранения в виде Xml.
Doc>Затем чуствуется в этой логике дыра. Вы изначально используете абстракцию, но потом приводите к конкоретным реализациям.
Ну а как иначе? У меня модель любого типа может ссылаться на модель любого типа через коннекторы.
Doc>Ну а по теме — вроде само логичное тут enum. Но я бы все же пересмотрел архитектуру.
enum, конечно, предпочтительное, но это похоже на выбор наименьшего дерьма. Архитектура уже сто раз пересматривалась, более оптимального пути пока что не вижу.
Здравствуйте, ksg71, Вы писали:
K>заведите "реестр" алгоритмов(стратегий)
K>
K>interface IRegistry<T> where T : ModelBase
K>{
K> IStrategy Get(T model);
K>}
K>
K>реализацию постройте на базе словаря Type — IStrategy, где Type конкретный тип модели
Идея хорошая, но уж очень много точек в программе, которые используют разные алгоритмы для разных моделей. По сути получится так, что IStrategy придется реализовывать какому-то одному классу, в то время как они могут быть разбиты по разным классам.
Здравствуйте, hi_octane, Вы писали:
_>Если C#4, можно на динамиках:
_>
_>dynamic d = abstractInstance;
_>//динамик сам разрулит тип и вызовет нужный метод
_>ConcreteStrategy(d);
_>void ConcreteStrategy(ConcreteModel1 cm);
_>void ConcreteStrategy(ConcreteModel2 cm);
_>void ConcreteStrategy(ConcreteModel3 cm);
_>void ConcreteStrategy(AbstractModel am)
_>{
_> throw new NotImplementedException("Нет стратегии для модели типа " + am.GetType().Name);
_>}
_>
Здравствуйте, Злобастик, Вы писали:
З>Здравствуйте, ksg71, Вы писали:
K>>заведите "реестр" алгоритмов(стратегий)
K>>
K>>interface IRegistry<T> where T : ModelBase
K>>{
K>> IStrategy Get(T model);
K>>}
K>>
K>>реализацию постройте на базе словаря Type — IStrategy, где Type конкретный тип модели
З>Идея хорошая, но уж очень много точек в программе, которые используют разные алгоритмы для разных моделей. По сути получится так, что IStrategy придется реализовывать какому-то одному классу, в то время как они могут быть разбиты по разным классам.
это почему? много реализаций, разными классами, собраны в реестр и т.д.
Das Reich der Freiheit beginnt da, wo die Arbeit aufhört. (c) Karl Marx
Здравствуйте, ksg71, Вы писали:
K>Здравствуйте, Злобастик, Вы писали:
З>>Здравствуйте, ksg71, Вы писали:
K>>>заведите "реестр" алгоритмов(стратегий)
K>>>
K>>>interface IRegistry<T> where T : ModelBase
K>>>{
K>>> IStrategy Get(T model);
K>>>}
K>>>
K>>>реализацию постройте на базе словаря Type — IStrategy, где Type конкретный тип модели
З>>Идея хорошая, но уж очень много точек в программе, которые используют разные алгоритмы для разных моделей. По сути получится так, что IStrategy придется реализовывать какому-то одному классу, в то время как они могут быть разбиты по разным классам.
K>это почему? много реализаций, разными классами, собраны в реестр и т.д.
Да, я ступил. Тут ведь два интерфейса. Спасибо. Подумаю над этим.
Здравствуйте, hi_octane, Вы писали:
З>>Задача выглядит тривиальной, но я за 10 лет ни с чем подобным не сталкивался. Может быть, есть какие-нибудь варианты элегантного решения?
_>Если C#4, можно на динамиках:
_>
_>dynamic d = abstractInstance;
_>//динамик сам разрулит тип и вызовет нужный метод
_>ConcreteStrategy(d);
_>void ConcreteStrategy(ConcreteModel1 cm);
_>void ConcreteStrategy(ConcreteModel2 cm);
_>void ConcreteStrategy(ConcreteModel3 cm);
_>void ConcreteStrategy(AbstractModel am)
_>{
_> throw new NotImplementedException("Нет стратегии для модели типа " + am.GetType().Name);
_>}
_>
_>В более старых C# можно двойной диспетчеризацией.
_>Ну и само собой лучше всего на Nemerle
Здравствуйте, bessony, Вы писали:
B>А как с производительностью в данном варианте?
Бенчмарк DLR для случая топикстартера.
Количество вызовов (диспетчеризаций): 1 000 000.
Количество моделей: от 2 до 30.
Результаты (сперва DLR, затем цепочка if-ов)
Test 2: 00:00:00.1501894 00:00:00.0127041
Test 3: 00:00:00.1807162 00:00:00.0146960
Test 4: 00:00:00.2172946 00:00:00.0167803
Test 5: 00:00:00.2566256 00:00:00.0179131
Test 6: 00:00:00.2932858 00:00:00.0187023
Test 7: 00:00:00.2979244 00:00:00.0196321
Test 8: 00:00:00.3242752 00:00:00.0211403
Test 9: 00:00:00.3320123 00:00:00.0219701
Test 10: 00:00:00.3653084 00:00:00.0225925
Test 11: 00:00:00.4333419 00:00:00.0234339
Test 12: 00:00:00.4874044 00:00:00.0242877
Test 13: 00:00:00.5334538 00:00:00.0255024
Test 14: 00:00:00.5837708 00:00:00.0260052
Test 15: 00:00:00.6210437 00:00:00.0266966
Test 16: 00:00:00.6607010 00:00:00.0279926
Test 17: 00:00:00.6698717 00:00:00.0284594
Test 18: 00:00:00.7256114 00:00:00.0292975
Test 19: 00:00:00.7611713 00:00:00.0305656
Test 20: 00:00:00.8009269 00:00:00.0310508
Test 21: 00:00:00.8917549 00:00:00.0317411
Test 22: 00:00:00.8400355 00:00:00.0322965
Test 23: 00:00:00.8469869 00:00:00.0334863
Test 24: 00:00:00.8708858 00:00:00.0342951
Test 25: 00:00:00.9246261 00:00:00.0351796
Test 26: 00:00:00.9296106 00:00:00.0355774
Test 27: 00:00:00.9462336 00:00:00.0362442
Test 28: 00:00:00.9989730 00:00:00.0372382
Test 29: 00:00:01.0061178 00:00:00.0387295
Test 30: 00:00:01.0659175 00:00:00.0393628
Время охватывает диспетчеризацию всего набора объектов (т.е. 1 000 000 вызовов).
Для DLR измеряется:
dynamic d = x;
ConcreteStrategy(d);
Для статического кода измеряется:
if(x is ConcreteModel1)
ConcreteStrategy((ConcreteModel1) x);
else
if(x is ConcreteModel2)
ConcreteStrategy((ConcreteModel2) x);
else
ConcreteStrategy(x);
Номер теста соответствует количеству if-ов.
Код T4:
[spoil]
<#@ template language="C#" #>
using System;
using System.Linq;
using System.Diagnostics;
using System.Runtime.CompilerServices;
<# const int modelsCount = 30; #>
<# const int repeatsCount = 1000000; #>
abstract class AbstractModel
{
}
<# for (var i = 1; i <= modelsCount; ++i) { #>
sealed class ConcreteModel<#= i #> : AbstractModel
{
}
<# } #>
<# for (var j = 2; j <= modelsCount; ++j) { #>
static class Test<#= j #>
{
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ConcreteStrategy(AbstractModel model)
{
Debug.Assert(false);
}
<# for (var i = 1; i <= j; ++i) { #>
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ConcreteStrategy(ConcreteModel<#= i #> model)
{
}
<# } #>
public static void Run()
{
var random = new Random(<#= j * 100 #>);
var data = new AbstractModel[<#= repeatsCount #>];
for(var i = 0; i < data.Length; ++i)
data[i] = Utils.CreateModel(random.Next(<#= j #>));
Utils.Measure("Test <#= j #>", data,
x =>
{
dynamic d = x;
ConcreteStrategy(d);
},
x =>
{
<# for (var i = 1; i <= j; ++i) { #>
if(x is ConcreteModel<#= i #>)
ConcreteStrategy((ConcreteModel<#= i #>) x);
else
<# } #>
ConcreteStrategy(x);
});
}
}
<# } #>
static class Program
{
static void Main(string[] args)
{
<# for (var j = 2; j <= modelsCount; ++j) { #>
Test<#= j.ToString() #>.Run();
<# } #>
}
}
static class Utils
{
static Utils()
{
_modelCtors = new Func<AbstractModel>[]
{
<# for (var i = 1; i <= modelsCount; ++i) { #>
() => new ConcreteModel<#= i #>(),
<# } #>
};
}
public static void Measure(string title, AbstractModel[] data, Action<AbstractModel> a, Action<AbstractModel> b)
{
a(data[0]);
b(data[0]);
var timer = Stopwatch.StartNew();
foreach(var x in data)
a(x);
var aTime = timer.Elapsed;
timer.Restart();
foreach(var x in data)
b(x);
var bTime = timer.Elapsed;
Console.WriteLine("{0}:\t\t{1}\t\t{2}", title, aTime, bTime);
}
public static AbstractModel CreateModel(int number)
{
return _modelCtors[number]();
}
private static Func<AbstractModel>[] _modelCtors;
}
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, bessony, Вы писали:
B>>А как с производительностью в данном варианте?
H>Бенчмарк DLR для случая топикстартера.
H>Количество вызовов (диспетчеризаций): 1 000 000. H>Количество моделей: от 2 до 30.
H>Результаты (сперва DLR, затем цепочка if-ов) H>
Здравствуйте, bessony, Вы писали:
B>И кстати as в цепочке if-ов работал бы быстрее чем is.
Однако мне не верится в это По моему опыту забегов ncc.exe скорость их равна — JIT компилятор достаточно умен чтобы оптимизировать тестирование типа и последующее приведение к нему.