Используя Действие <T> как аргумент в C# (имитирующий указатель функции)

Я думаю, что всем не хватает настоящей проблемы. Дело не в методах доступа, а скорее в том, что NSOrderedSet не является подклассом в NSSet. Таким образом, когда -interSectsSet: вызывается с упорядоченным набором в качестве аргумента, это не получается.

NSOrderedSet* setA = [NSOrderedSet orderedSetWithObjects:@"A",@"B",@"C",nil];
NSSet* setB = [NSSet setWithObjects:@"C",@"D", nil];

 [setB intersectsSet:setA];

не удается с *** -[NSSet intersectsSet:]: set argument is not an NSSet

Похоже, исправление состоит в том, чтобы изменить реализацию операторов множеств, чтобы они прозрачно обрабатывали типы. Нет причин, по которым -intersectsSet: должен работать с упорядоченным или неупорядоченным множеством.

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

Следующее помогло мне

@implementation MF_NSOrderedSetFixes

+ (void) fixSetMethods
{
    NSArray* classes = [NSArray arrayWithObjects:@"NSSet", @"NSMutableSet", @"NSOrderedSet", @"NSMutableOrderedSet",nil];

    [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* name = obj;
        Class aClass = objc_lookUpClass([name UTF8String]);
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(intersectsSet:) forClass:aClass];
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(isSubsetOfSet:) forClass:aClass];
    }];
}

typedef BOOL (*BoolNSetIMP)(id _s,SEL sel, NSSet*);

/*
    Works for all methods of type - (BOOL) method:(NSSet*) aSet
*/
+ (void) fixMethodWithSetArgument:(SEL) aSel forClass:(Class) aClass 
{
    /* Check that class actually implements method first */
    /* can't use get_classInstanceMethod() since it checks superclass */
    unsigned int count,i;
    Method method = NULL;
    Method* methods = class_copyMethodList(aClass, &count);
    if(methods) {
        for(i=0;i<count;i++) {
            if(method_getName(methods[i])==aSel) {
                method = methods[i];
            }
        }
        free(methods);
    }
    if(!method) {
        return;
    }

   // Get old implementation
   BoolNSetIMP originalImp  = (BoolNSetIMP) method_getImplementation(method);
   IMP newImp = imp_implementationWithBlock(^BOOL(NSSet *_s, NSSet *otherSet) {
        if([otherSet isKindOfClass:[NSOrderedSet class]]) {
            otherSet = [(NSOrderedSet*)otherSet set];
        }
        // Call original implementation
        return originalImp(_s,aSel,otherSet);
    });
    method_setImplementation(method, newImp);
}
@end
6
задан cgyDeveloper 30 June 2009 в 20:44
поделиться

4 ответа

Если вы хотите, чтобы это было достаточно универсальным, чтобы обрабатывать любое количество аргументов, попробуйте использовать неуниверсальный делегат Action:

protected void udpCommand(Action command)
{
    while(!linkDownFail)
    {
        try
        {
            command();
            break;
        }
        catch
        {
            LinkStateCallBack(ip, getLinkStatus());
            if (linkDownFail) throw new LinkDownException();
            Thread.Sleep(100);
        }
    }
    return;
}

В C # 3.0 вы можете вызвать его следующим образом:

udpCommand(() => noParameterMethod());
udpCommand(() => singleParameterMethod(value));
udpCommand(() => manyParameterMethod(value, value2, value3, value4));

В C # 2.0 это немного уродливее:

udpCommand(delegate { noParameterMethod(); });
udpCommand(delegate { singleParameterMethod(value); });
udpCommand(delegate { manyParameterMethod(value, value2, value3, value4); });

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

EDIT

Я просто заметил, что я как бы украл комментарий Марка Гравелла ... извинения, Марк. Чтобы ответить на вопрос, как можно уменьшить дублирование, вы можете заставить метод Action вызывать метод Func , например:

protected void udpCommand(Action command)
{
    udpCommand(() => { command(); return 0; });
}

Я считаю (и могу ошибаться), что возвращение 0 не более затратно, чем (неявно) возвращение void, но, возможно, я здесь далеко. Даже если у этого есть своя стоимость, он поместит в стек лишь крошечную мелочь. В большинстве случаев дополнительные расходы не причинят вам никакого вреда.

10
ответ дан 8 December 2019 в 14:45
поделиться

Вы имеете в виду:

    protected void udpCommand<T>(Action<T> command, T value) {...}

С вызовом:

udpCommand(someUdpCommand, arg);

Обратите внимание, что это может работать лучше на C # 3.0, который имеет более сильный вывод универсального типа, чем C # 2.0.

4
ответ дан 8 December 2019 в 14:45
поделиться

Я думаю, вам просто нужно вынуть (значение T) после "команды".

2
ответ дан 8 December 2019 в 14:45
поделиться

Вы пытаетесь сделать это ...

protected void udpCommand<T>(Action<T> command, T value)
{
   while(!linkDownFail)
   {
    try                
    {
      command(value);
      // etc.
    }
  }
}

Тогда это будет работать так ...

public void ActionWithInt( int param )
{
   // some command
}

Action<int> fp = ActionWithInt;

udpCommand<int>( fp, 10 );  // or whatever.
0
ответ дан 8 December 2019 в 14:45
поделиться
Другие вопросы по тегам:

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