Использование Generics для реализации упрощенного объектного интерфейса - PullRequest
1 голос
/ 11 июля 2011

Справочная информация: Я работаю с очень большим решением, которое имеет много сложных компонентов пользовательского интерфейса. В целях отслеживания ошибок мы хотели бы иметь возможность регистрировать любые действия, предпринимаемые пользователем, чтобы мы могли воссоздать (каким-либо образом в сценарии) любую ситуацию, с которой пользователь может столкнуться при использовании графического интерфейса пользователя. Чтобы достичь этой цели, мы хотели бы создать стандарт, который заставляет любой системный компонент, который используется любым компонентом пользовательского интерфейса, реализовывать очень простой интерфейс. Интерфейс будет иметь методы Get, Set и Run. (Журналы / сценарии будут создаваться этими методами.) Все остальные члены производного класса будут частными, за исключением этих методов Get, Set и Run.

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

Код компилируется и запускается, но я знаю, что не очень хорошо это реализую. Вероятно, я не экономлю на расходах в функциях Get или Set, используя Convert.ChangeType поверх простого приведения объекта. Я также уничтожаю безопасность типов, которую должны предлагать дженерики.

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

public class TestClass {
public enum SetEnum { Val1, Val2 }
public enum GetEnum { Calculated, Result }
public enum RunEnum { Add, Subtract, Reset }

private double Val1;
private int Val2;
private bool Calculated;
private double Result;

public void Set<T>(SetEnum member, T val) {
    switch (member) {
    case SetEnum.Val1:
        Val1 = (double)Convert.ChangeType(val, Type.GetTypeCode(typeof(double)));
        break;
    case SetEnum.Val2:
        Val2 = (int)Convert.ChangeType(val, Type.GetTypeCode(typeof(int)));
        break;
    default:
        throw new ArgumentOutOfRangeException("variable");
    }
}

public T Get<T>(GetEnum member) {
    switch (member) {
        case GetEnum.Calculated: return (T)Convert.ChangeType(Calculated, Type.GetTypeCode(typeof(T)));
        case GetEnum.Result: return (T)Convert.ChangeType(Result, Type.GetTypeCode(typeof(T)));
        default: throw new ArgumentOutOfRangeException("member");
    }
}

public void Run(RunEnum member) {
    switch (member) {
        case RunEnum.Add: Add(); break;
        case RunEnum.Subtract: Subtract(); break;
        case RunEnum.Reset: Reset(); break;
        default: throw new ArgumentOutOfRangeException("member");
    }
}

private void Add() {
    Result = Val1 + Val2;
    Calculated = true;
}

private void Subtract() {
    Result = Val1 - Val2;
    Calculated = true;
}

private void Reset() {
    Result = 0;
    Calculated = false;
}
}

Ответы [ 2 ]

1 голос
/ 11 июля 2011

Вместо этого я бы использовал нормальные интерфейсы.Это намного чище и проще.

И поместите прокси между реальной реализацией интерфейса и интерфейсом, который видит пользовательский интерфейс.Этот прокси может динамически генерироваться во время выполнения и может вести регистрацию.Я уверен, что есть некоторые библиотеки, которые могут генерировать прокси для вас, но благодаря Expression s это не так уж сложно написать самостоятельно.

под обычным интерфейсом я имею в виду что-то вроде следующего:

interface MyInterface
{
    double Value1{get;set;}
    void Add();
    void Sub();
}

Так же, как вы написали бы любой интерфейс.Затем реализуйте это в обычном классе.Я не использовал прокси-генератор самостоятельно, но я уверен, что некоторые насмешливые и AoP-фреймворки поддерживают это.Быстрый google для dynamic proxy C# показывает http://joe.truemesh.com/blog//000181.html, который утверждает, что NMock поддерживает это.

Альтернативой является использование основанной на перезаписи IL инфраструктуры AOP, такой как PostSharp.Вы указываете его на свои сборки (как часть процесса сборки), и он автоматически добавляет в него входящие вызовы.

0 голосов
/ 11 июля 2011

Еще одно решение: просто добавьте базовый класс с виртуальными методами и любым «компонентом», который его реализует, переопределите его конкретной реализацией и вызовите методы базового класса, где вы на самом деле пишете материал для журналирования.

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

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...