通过反射处理通用属性

时间:2016-03-29 06:31:54

标签: c# generics reflection

如果我有以下包装类:

public class Wrapper<T>
{
    public T Data { get; set; }
    public string[] Metadata { get;set;
}

然后另一个类在没有泛型的情况下公开该值:

public class SomeOtherClass
{
    public object WrappedData { get;set };
}

,我如何获得原始的未包装数据?

我可以使用以下内容进行测试:

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Wrapper<>))
{
    dynamic originalValue = someOtherClass.WrappedData;
}

但我无法调用.Data上的originalValue媒体资源,获得RuntimeBinderException

更新

更多的背景可能会有所帮助。我正在开发一个WebAPI,我想要实现HATEOAS。所以我的包装器类包含将返回的数据和元数据,我正在编写一个动作过滤器,它将解包数据,将其返回到响应主体中,并将​​元数据放入响应头。动作过滤器目前实现如下:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    if (actionExecutedContext.Request.Method == HttpMethod.Get)
    {
        var objectContent = actionExecutedContext.Response.Content as ObjectContent;
        if (objectContent != null)
        {
        var type = objectContent.ObjectType;
        var formatter = actionExecutedContext
            .ActionContext
            .ControllerContext
            .Configuration
            .Formatters
            .First(f => f.SupportedMediaTypes
                .Contains(new MediaTypeHeaderValue(actionExecutedContext
                    .Response
                    .Content
                    .Headers
                    .ContentType
                    .MediaType)));

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Wrapper<>))
            {
                dynamic value = objectContent.Value;
                actionExecutedContext.Response.Content = new ObjectContent(value.Data.GetType(), value.Data, formatter);
            }
        }
    }

    base.OnActionExecuted(actionExecutedContext);
}

显然,我的所有API端点当前都没有包装他们的数据,所以如果响应没有返回Wrapper<T>实例,我想退出操作过滤器而不修改响应。如果是,则拉出.Data的值并用它重写响应主体。

4 个答案:

答案 0 :(得分:1)

以下代码有效:

using System;

namespace ConsoleApplication1
{
    class Program
    {

        public class Wrapper<T>
        {
            public T Data { get; set; }
            public string[] Metadata
            {
                get; set;
            }

        }

        public class SomeOtherClass
        {
            public object WrappedData { get; set; }
        }


        static void Main(string[] args)
        {
            var wrappedData = new Wrapper<int> { Data = 3 };
            var someObject = new SomeOtherClass { WrappedData = wrappedData };

            dynamic d = someObject.WrappedData;
            Console.WriteLine(d.Data);

        }
    }
}

所以,目前尚不清楚你的问题是什么!

答案 1 :(得分:1)

从发布的代码中不清楚objectContent.ObjectType是什么,所以我会修改代码以检查实际值:

object value = objectContent.Value;
if (value != null && value.GetType().IsGenericType && value.GetType().GetGenericTypeDefinition() == typeof(Wrapper<>))
{
    object data = ((dynamic)value).Data;
    actionExecutedContext.Response.Content = new ObjectContent(data.GetType(), data, formatter);
}

但是,如果使用非通用接口备份泛型类,则可以避免反射和动态调用,并使您的生活更轻松。例如

public interface IWrapper
{
    object Data { get; }
    string[] Metadata { get; }
}

public class Wrapper<T> : IWrapper
{
    public T Data { get; set; }
    object IWrapper.Data { get { return Data; } }
    public string[] Metadata { get; set; }
}

然后你可以做简单的

var wrapper = objectContent.Value as IWrapper;
if (wrapper != null)
{
    actionExecutedContext.Response.Content = new ObjectContent(wrapper.Data.GetType(), wrapper.Data, formatter);
}

答案 2 :(得分:0)

如果将类型添加到包装器中会有帮助吗?

public class Wrapper<T>
{
    public Type MyType{get;set;}
    public T Data { get; set; }
    public string[] Metadata { get;set;}

    public Wrapper(T data){
      MyType = data.GetType();
      Data = data; 
    }
}

答案 3 :(得分:0)

不确切地知道你要做什么:

object obj = new Wrapper<SomeOtherClass> { Data = new SomeOtherClass { WrappedData = "Hello" } };
Type type = obj.GetType();

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Wrapper<>))
{
    dynamic data = ((dynamic)obj).Data;
    dynamic wrappedData = data.WrappedData;
}

请注意,无法保证WrappedData属性中包含Data属性:WrappedData属于SomeOtherClass属性,但例如obj可能是:

object obj = new Wrapper<string> { Data = "Hello" };