Существует ли простой способ эмулировать Категории Objective C в C#?

areas = json.stores.map(function (x) {
    return x.storeArea
});
dates = json.stores.map(function (x) {
    return x.totalStore.map(function (y) {
        return y.date
    })
});
results = [areas,dates.flat()]

Результаты - -

(2) [Array(6), Array(18)]
0: (6) ["area1", "area2", "area3", "area4", "area5", "area6"]
1: (18) ["2018-10-01", "2018-11-01", "2018-12-01", "2018-10-01", "2018-11-01", "2018-12-01", "2018-10-01", "2018-11-01", "2018-12-01", "2018-10-01", "2018-11-01", "2018-12-01", "2018-10-01", "2018-11-01", "2018-12-01", "2018-10-01", "2018-11-01", "2018-12-01"]
6
задан Bruce Johnston 8 January 2009 в 16:36
поделиться

2 ответа

Я собираюсь попробовать... ;-) Если в Вашей системе существует фаза при заполнении "каталога" IScanner объекты Вы могли думать об украшении Вашего IScanners с атрибутом, указывающим, который Part они интересуются. Затем можно отобразить эту информацию и управлять сканированием Вашего Composite с картой. Это не полный ответ: если у меня будет немного времени, то я попытаюсь уточнить...

Править: немного псевдокода для поддержки моего запутанного объяснения

public interface IScanner
{
    void Scan(IPart part);
}

public interface IPart
{
    string ID { get; }
}

[ScannedPart("your-id-for-A")]
public class AlphaScanner : IScanner
{
    public void Scan(IPart part)
    {
        throw new NotImplementedException();
    }
}

[ScannedPart("your-id-for-B")]
public class BetaScanner : IScanner
{
    public void Scan(IPart part)
    {
        throw new NotImplementedException();
    }
}

public interface IComposite
{
    List<IPart> Parts { get; }
}

public class ScannerDriver
{
    public Dictionary<string, IScanner> Scanners { get; private set; }

    public void DoScan(IComposite composite)
    {
        foreach (IPart part in composite.Parts)
        {
            IScanner scanner = Scanners[part.ID];
            scanner.Scan(part);
        }
    }
}

Не берите его как есть: это для цели объяснения.

Править: ответьте на комментарии полковника Kernel. Я рад, что Вы находите это интересным. :-) В этом простом эскизе кода отражение должно быть включено только во время инициализации Словаря (или, когда необходимый), и во время этой фазы можно "осуществить" присутствие атрибута (или даже использовать другие способы отобразить сканеры и части). Я говорю, "осуществляют", потому что, даже если это не ограничение времени компиляции, я думаю, что Вы выполните свой код, по крайней мере однажды вводящий его в эксплуатацию ;-) таким образом, это могло быть ограничение во время выполнения в случае необходимости. Я сказал бы, что вдохновение - что-то (очень очень слегка) подобный MEF или другим подобным платформам. Просто мои 2 цента.

1
ответ дан 17 December 2019 в 20:36
поделиться

Это походит на то, что Вы говорите, то, что у Вас есть содержание, разметил что-то вроде этого:

+--------+
| part 1 |
| type A |
+--------+
| part 2 |
| type C |
+--------+
| part 3 |
| type F |
+--------+
| part 4 |
| type D |
+--------+

и у Вас есть читатели для каждого типа детали. Таким образом, AScanner знает, как обработать данные в части типа A (такого как часть 1 выше), BScanner знает, как обработать данные в части типа B и т.д. Действительно ли я прав до сих пор?

Теперь, если я понимаю Вас, проблема, которую Вы имеете, состоит в том что читатели типа ( IScanner реализации), не знают, как определить местоположение части (частей), которую они распознают в Вашем составном контейнере.

Ваш составной контейнер может правильно перечислить отдельные части (т.е. он знает, где концы части и другой начинают), и если так, вносит свой вклад, имеют своего рода идентификацию, которую могут дифференцировать или сканер или контейнер?

То, что я имею в виду, данные размечаются что-то вроде этого?

+-------------+
| part 1      |
| length: 100 |
| type: "A"   |
| data: ...   |
+-------------+
| part 2      |
| length: 460 |
| type: "C"   |
| data: ...   |
+-------------+
| part 3      |
| length: 26  |
| type: "F"   |
| data: ...   |
+-------------+
| part 4      |
| length: 790 |
| type: "D"   |
| data: ...   |
+-------------+

Если Ваш формат данных подобен этому, разве сканеры не могли бы запросить контейнера, все расстается с идентификатором, соответствующим данному шаблону? Что-то как:

class Container : IContainer{
    IEnumerable IContainer.GetParts(string type){
        foreach(IPart part in internalPartsList)
            if(part.TypeID == type)
                yield return part;
    }
}

class AScanner : IScanner{
    void IScanner.ProcessContainer(IContainer c){
        foreach(IPart part in c.GetParts("A"))
            ProcessPart(part);
    }
}

Или, если бы, возможно, контейнер не смог бы распознать тип детали, но сканер смог бы распознать свой собственный тип детали, возможно, что-то как:

delegate void PartCallback(IPart part);

class Container : IContainer{
    void IContainer.GetParts(PartCallback callback){
        foreach(IPart part in internalPartsList)
            callback(part);
    }
}

class AScanner : IScanner{
    void IScanner.ProcessContainer(IContainer c){
        c.GetParts(delegate(IPart part){
            if(IsTypeA(part))
                ProcessPart(part);
        });
    }

    bool IsTypeA(IPart part){
        // determine if part is of type A
    }
}

Возможно, я неправильно понял Ваше содержание и/или Вашу архитектуру. Если так, разъяснитесь, и я обновлю.


Комментарий от OP:

  1. Сканеры не должны иметь никакого знания контейнерного типа.
  2. Контейнерный тип не обладает никаким встроенным интеллектом. Это как близко к простым данным, поскольку можно войти в C#.
  3. Я не могу изменить контейнерный тип; Это - часть существующей архитектуры.

Мои ответы являются слишком длинными для комментариев:

  1. Сканеры должны иметь некоторый способ получить часть (части), которую они обрабатывают. Если Ваше беспокойство то, что IScanner интерфейс не должен знать IContainer взаимодействуйте через интерфейс так, чтобы у Вас была свобода измениться IContainer интерфейс в будущем, затем Вы могли пойти на компромисс одним из нескольких способов:

    • Вы могли передать сканеры IPartProvider соедините интерфейсом с этим IContainer полученный из (или содержавший). Это IPartProvider только обеспечил бы функциональность подавания частей, таким образом, это должно быть довольно стабильно, и это могло быть определено в том же блоке как IScanner, так, чтобы Ваши плагины не должны были бы ссылаться на блок где IContainer был определен.
    • Вы могли передать делегата в сканерах, которые они могли использовать для получения частей. Сканерам затем не было бы нужно знание никакого интерфейса безотносительно (кроме IScanner, конечно), только делегата.
  2. и

  3. Это походит, возможно, что Вам нужен суррогатный класс, который знает, как общаться с и контейнером и сканерами. Какая-либо упомянутая выше функциональность могла быть реализована в каком-либо старом классе, пока контейнер уже выставляет достаточно функциональности публично (или protectedly [который является словом?]), что внешняя сторона/производный класс смогла бы получить доступ к соответствующим данным.

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

Я сказал бы, что это - Ваша основная проблема. Необходимо отделить плагины — который я принимаю, конструкторы IScanner интерфейс — из главного приложения — который я принимаю, то, где CompositeScanner жизни класса. Одно из моих более ранних предложений - то, как я реализовал бы это, но точные детали зависят от как Ваш parseTypeX работ функций. Они могут быть абстрагированы и обобщены?

По-видимому, Ваш parseTypeX функций общаются с Composite объект класса для получения данных им нужно. Могли они не быть перемещенными в a Parse метод на IScanner интерфейс, который проксировал через CompositeScanner класс для получения этих данных из Composite объект? Что-то вроде этого:

delegate byte[] GetDataHandler(int offset, int length);

interface IScanner{
    void   Scan(byte[] data);
    byte[] Parse(GetDataHandler getData);
}

class Composite{
    public byte[] GetData(int offset, int length){/*...*/}
}

class CompositeScanner{}
    IScanner realScanner;

    public void ScanComposite(Composite c){
        realScanner.Scan(realScanner.Parse(delegate(int offset, int length){
            return c.GetData(offset, length);
        });
    }
}

Конечно, это могло быть упрощено путем удаления отдельного Parse метод на IScanner и просто передача GetDataHandler делегируйте непосредственно к Scan (чья реализация могла назвать частное Parse, при желании). Код затем выглядит очень похожим на мои более ранние примеры.

Этот дизайн обеспечивает столько же разделения проблем и отделения, о котором я могу думать.


Я просто думал о чем-то еще, что Вы могли бы найти более приемлемым, и действительно, можете обеспечить лучшее разделение проблем.

Если у Вас может быть каждый сменный "регистр" с приложением, можно, возможно, оставить парсинг в рамках приложения, пока плагин может сказать приложение, как получить его данные. Примеры ниже, но так как я не знаю, как Ваши части определяются, я реализовал две возможности — один для индексируемых частей и один для именованных частей:

// parts identified by their offset within the file
class MainApp{
    struct BlockBounds{
        public int offset;
        public int length;

        public BlockBounds(int offset, int length){
            this.offset = offset;
            this.length = length;
        }
    }

    Dictionary<Type, BlockBounds> plugins = new Dictionary<Type, BlockBounds>();

    public void RegisterPlugin(Type type, int offset, int length){
        plugins[type] = new BlockBounds(offset, length);
    }

    public void ScanContent(Container c){
        foreach(KeyValuePair<Type, int> pair in plugins)
            ((IScanner)Activator.CreateInstance(pair.Key)).Scan(
                c.GetData(pair.Value.offset, pair.Value.length);
    }
}

или

// parts identified by name, block length stored within content (as in diagram above)
class MainApp{
    Dictionary<string, Type> plugins = new Dictionary<string, Type>();

    public void RegisterPlugin(Type type, string partID){
        plugins[partID] = type;
    }

    public void ScanContent(Container c){
        foreach(IPart part in c.GetParts()){
            Type type;
            if(plugins.TryGetValue(part.ID, out type))
                ((IScanner)Activator.CreateInstance(type)).Scan(part.Data);
        }
    }
}

Очевидно, я чрезвычайно упростил эти примеры, но надо надеяться, Вы получаете идею. Кроме того, вместо использования Activator.CreateInstance, было бы хорошо, если Вы могли бы передать фабрику (или делегат фабрики) к RegisterPlugin метод.

1
ответ дан 17 December 2019 в 20:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: