【问题标题】:Handling generic properties via reflection通过反射处理泛型属性
【发布时间】:2016-03-29 15:30:41
【问题描述】:

如果我有以下包装类:

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

但我不能在originalValue 上调用.Data 属性,得到RuntimeBinderException


更新

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

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&lt;T&gt; 实例,我想退出操作过滤器而不修改响应。如果是,则取出.Data的值,并用它重写响应体。

【问题讨论】:

  • WrapperData 包含什么?如果它只包含Wrapper&lt;T&gt;.Data 的值,那就很清楚了。如果它包含整个包装器,我们应该在初始化方面寻找问题。
  • 什么包含type变量?

标签: c# generics reflection


【解决方案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】:

以下代码有效:

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

        }
    }
}

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 我如何获得 MyType 属性?它与 Data 属性一样难以访问。如何将 SomeOtherClass.WrappedData 实例实例化或强制转换为 Wrapper
    • 是的,这是个好问题。对不起,我误读了你的原帖。顺便问一下为什么需要做这个技巧是否有意义?在 SOLID 设计的这些日子里,拳击值感觉有点奇怪 - 我很好奇,你可能有一个很好的理由这样做:)
    • 您看过以下内容吗? msdn.microsoft.com/en-us/magazine/…
    • 是的,我有;但是,我希望能够解开原始数据,并在响应标头中返回元数据,而不是在响应正文中创建信封。
    【解决方案4】:

    不清楚你到底想做什么:

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

    请注意,不能保证Data 属性中存在WrappedData 属性:WrappedDataSomeOtherClass 的属性,但例如obj 可能是:

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

    【讨论】:

      猜你喜欢
      • 2016-05-10
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      相关资源
      最近更新 更多