简体   繁体   English

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

[英]Using a generic C# interface as a class property

I have an interface 我有一个界面

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

and a class 和一堂课

public class ToDoTask 
{
    private int someInt;

    public string StringProperty {get; set;}

    public ICallback Callback {get; private set;}
}

This doesn't compile because it wants me to specialize the Callback property. 这不会编译,因为它要我专门化Callback属性。 I don't want to specialize the property because I don't know the type. 我不想专门研究该属性,因为我不知道类型。 The type is determined at runtime through reflection. 类型是在运行时通过反射确定的。

Here's a small explanation of what happens: 以下是发生的情况的简短说明:

  1. A proxy class is generated (via CreateInterfaceProxyWithoutTarget<C> of Castle DynamicProxy ) at runtime to implement a given interface (Let's call this IModel ) 在运行时生成一个代理类(通过Castle DynamicProxy的 CreateInterfaceProxyWithoutTarget<C> )来实现给定的接口(我们将此称为IModel
  2. A call is made to a method of this proxy class (remember, there's no target so there's no code that executes here aside from what my interceptor does) 对此代理类的方法进行了调用(请记住,没有目标,因此除了我的拦截器所做的以外,这里没有代码可以执行)
  3. The method calls are intercepted by a main class that decides what needs to be done based on the attributes on the method and it's parameters (as defined in IModel ) 方法调用被主类拦截,该主类根据方法的属性及其参数(如IModel定义)来决定需要执行的操作。
  4. At this point, if the last parameter of the intercepted method is an ICallback<T> , it gets assigned to the Callback property, otherwise it's null . 此时, 如果所拦截方法的最后一个参数是ICallback<T> ,则会将其分配给Callback属性,否则为null
  5. If the callback is not null, the operation result is parsed and the appropriate callback method is called. 如果回调不为 null,则将解析操作结果并调用适当的回调方法。

This is what the Interceptor class looks like: 这是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);
        }
    }
}

Is it safe to declare the callback as ICallback<object> and cast & return at runtime? 将回调声明为ICallback<object>并在运行时ICallback<object>并返回是否安全?

If any of the above sounds stupid, its because I don't have much experience with reflection in C# and I'm diving deep into c# after about 4 years of not touching it. 如果上述方法中的任何一个听起来很愚蠢,那是因为我对C#的反射没有太多经验,并且在接触了大约4年之后我将深入研究C#。 Be nice! 对人好点!

You need to define what T is, or use another option. 您需要定义T是什么,或使用其他选项。

The three simplest ways to fix this would be: 解决此问题的三种最简单方法是:

Have T defined by ToDoTask<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;}
}

Define the OnSuccess method as generic, rather than the interface 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; }
}

Use object instead of T 使用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;}
}

ICallBack does not exist, it must be ICallBack<T> so you need to provide a type - or make ToDoTask generic and pass the type to ICallBack . ICallBack不存在,它必须是ICallBack<T>因此您需要提供一个类型-或使ToDoTask通用并将该类型传递给ICallBack

like so: 像这样:

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

And then pass in the type after reflection or whatever. 然后在反射或其他之后传递类型。

A less safer alternative is to use dynamic . 较不安全的替代方法是使用dynamic Here is a sample program that is trying to do something similar to what you're trying 这是一个示例程序,正在尝试执行与您尝试的操作类似的操作

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();
    }
}

Generally, since generics is a compile time feature, you will need to specify the time at compile time. 通常,由于泛型是compile time功能,因此您需要在编译时指定时间。

When you use reflection(runtime), you would prefer working with base types and use polymorphism to accomplish things like this. 使用反射(运行时)时,您更喜欢使用基本类型并使用polymorphism来完成类似的工作。

I didn't understand by 100% what you wanted to do but you can: 我100%不了解您想做什么,但是您可以:

1.Use an ICallback<DeriveType> and work with polymorphism or object in the worst case(be carefull to avoid boxing\\unboxing of value types). 1.使用ICallback<DeriveType>并在最坏的情况下使用polymorphismobject (请小心, 避免对值类型进行装箱\\拆箱)。

2.Create an non generic interface that will look something like this: 2.创建一个非通用接口,看起来像这样:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM