使用通用C#接口作为类属性

时间:2014-09-28 06:14:59

标签: c#

我有一个界面

public interface ICallback<T> 
{
    void OnSuccess (T data);
    void OnFailure (Exception exception);
}

和一个班级

public class ToDoTask 
{
    private int someInt;

    public string StringProperty {get; set;}

    public ICallback Callback {get; private set;}
}

这不会编译,因为它要我专门化Callback属性。我不想专门从事物业,因为我不知道这种类型。该类型在运行时通过反射确定。

以下是对所发生情况的一个小解释:

  1. 在运行时生成代理类(通过{em> Castle DynamicProxy 的CreateInterfaceProxyWithoutTarget<C>)以实现给定的接口(让我们调用此IModel
  2. 调用此代理类的方法(记住,没有目标,所以除了我的拦截器之外没有代码在这里执行)
  3. 方法调用由主类拦截,主类根据方法的属性及其参数(在IModel中定义)决定需要完成的操作
  4. 此时, 如果 截获方法的最后一个参数是ICallback<T>,则会将其分配给Callback属性,否则它是null
  5. 如果回调是而不是 null,则会解析操作结果并调用相应的回调方法。
  6. 这就是Interceptor类的样子:

    public class Intercepteur : IInterceptor{
        public void Intercept(IInvocation invocation) {
            ToDoTask task = Study(invocation);
    
            /* 
               nothing past this point is actually written, 
               so there may be typos or compile errors below
            */
    
            try 
            {
                var result = Perform(task);
    
                if (null != task.Callback)
                   task.Callback.OnSuccess (MagicalTransformation (result, typeof (T)));
            } catch (Exception e) 
            {
                if (null != task.Callback)
                    task.Callback.OnFailure (e);
            }
        }
    }
    

    将回调声明为ICallback<object>并强制转换是否安全?在运行时返回?

    如果上述任何一个听起来很愚蠢,那是因为我在C#中没有太多的反思经验,而且在没有碰到它的大约4年后我深入c#潜水。太好了!

3 个答案:

答案 0 :(得分:3)

您需要定义T是什么,或使用其他选项。

解决这个问题的三种最简单方法是:

T

定义ToDoTask<T>
public interface ICallback<T> {
    void OnSuccess (T data);
    void OnFailure (Exception exception);
}

public class ToDoTask<T> {
    private int someInt;

    public string StringProperty {get; set;}

    public ICallback<T> Callback {get; private set;}
}

OnSuccess方法定义为通用,而不是界面

public interface ICallback
{
    void OnSuccess<T>(T data);
    void OnFailure(Exception exception);
}

public class ToDoTask<T>
{
    private int someInt;

    public string StringProperty { get; set; }

    public ICallback Callback { get; private set; }
}

使用object代替T

public interface ICallback {
    void OnSuccess (object data);
    void OnFailure (Exception exception);
}

public class ToDoTask {
    private int someInt;

    public string StringProperty {get; set;}

    public ICallback Callback {get; private set;}
}

答案 1 :(得分:1)

ICallBack不存在,必须为ICallBack<T>,因此您需要提供类型 - 或使ToDoTask泛型并将类型传递给ICallBack

像这样:

class ToDoTask<T>{
   ...
   ICallBack<T> CallBack {get;set;}
}

然后在反射之后传递类型或其他任何内容。

不太安全的选择是使用dynamic。这是一个示例程序,试图执行类似于您尝试的操作

class Program
{
    interface ICallBack<T>
    {
        void SetObject(T obj);
    }

    class CallBackUser<T>
    {
        public dynamic CB { get; set; }
    }

    class CB<T> : ICallBack<T>
    {
        public void SetObject(T obj)
        {
            Console.WriteLine("Works");
        }
    }

    static Type GetMyTypeThroughReflection()
    {
        return typeof (int);
    }
    static void Main(string[] args)
    {
        Type t = GetMyTypeThroughReflection();
        Type genericClass = typeof(CallBackUser<>);
        Type constructedClass = genericClass.MakeGenericType(t);

        dynamic created = Activator.CreateInstance(constructedClass);
        created.CB = new CB<int>();
        created.CB.SetObject(0);
        Console.ReadKey();
    }
}

答案 2 :(得分:0)

通常,由于泛型是compile time特性,因此您需要在编译时指定时间。

当你使用反射(运行时)时,你更喜欢使用基类型并使用polymorphism来完成这样的事情。

我100%不理解你想做什么,但你可以:

1.在最坏的情况下使用ICallback<DeriveType>并使用polymorphismobject(小心避免装箱\取消装箱值类型)。

2.创建一个非通用的界面,如下所示:

public interface ICallback {
    void OnSuccess (DeriveType data);
    void OnFailure (Exception exception);
}