开发者

Using Generics to implement simplified object interface

Background: I am working with a very large solution that has many complicated UI components. For error tracking purposes, we would like to have the ability to log any action that a user takes so that we can recreate (in some scriptable manner) any situation that a user could possibly run into when using the GUI. To achieve this goal, we would like to create a standard that forces any system component that is used by any UI component to implement a very simple interface. The interface would have Get, Set and Run methods. (Logs/scripts would be generated by these methods.) All other members of the derived class would be private with the exception of these Get, Set and Run methods.

The problem: I am trying to think of a good way to implement such an interface and have to admit that I am struggling with some of the abstract concepts required to pull this off. I know how I could do it using objects, but I know that casting objects can be expensive and the solution would not be type safe. I have just started to learn about generics and it seems like that might be the best road to go down, but I’m struggling with the implementation. Below is some sample code that I was playing with. For simplicity, it is not actually implementing an interface, but it is structured like it would need to be.

The code compiles and runs, but I know I’m not implementing this very well. I am probably not saving any expense in the Get or Set functions by using Convert.ChangeType over a simple object cast. I am also destroying the type safety that generics are supposed to offer.

While suggestions on any portion of this are welcome, the best answer will probably be awarded to the solution that shows how I should be using generics to properly implement this. Here is the code that I am playing with to illustrate the concept so far:

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 开发者_JAVA百科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;
}
}


I'd use normal interfaces instead. That's much cleaner and easier.

And the put a proxy between the real implementation of the interface and the interface the UI sees. That proxy can be dynamically generated at runtime and can do the logging. I'm sure there are some existing libraries that can generate the proxy for you, but thanks to Expressions it's not that hard to write it yourself either.

by normal interface I mean something like the following:

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

Just like you'd write any interface. Then implement it in a normal class. I haven't used any proxy generator myself, but I'm sure several mocking and AoP frameworks support that. A quick google for dynamic proxy C# turns up http://joe.truemesh.com/blog//000181.html which claims that NMock supports this.

An alternative is using an IL rewrite based AOP framework like PostSharp. You point it at your assemblies(as part of the build process) and it automatically adds logging calls into it.


Another solution yet: just add base class with virtual methods and any "component" that implements it, overrides it with concrete implementation and call base class method's where you actually write logging stuff.

Well you need to know the "ID" of component. That could be virtual property too where every concrete class assigns its unique "ID".

Hope this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜