【问题标题】:Getting the value of a generic property at run time在运行时获取通用属性的值
【发布时间】:2018-08-06 06:55:10
【问题描述】:

我使用代理和拦截来记录日志。我要记录的属性之一是来自 rabbit MQ 的消息 ID。

我们正在使用以下对象:

namespace MassTransit
{
    public interface ConsumeContext<out T> : ConsumeContext, MessageContext, PipeContext, IPublishEndpoint, IPublishObserverConnector, ISendEndpointProvider where T : class
    {
        T Message { get; }

        /// <summary>Notify that the message has been consumed</summary>
        /// <param name="duration"></param>
        /// <param name="consumerType">The consumer type</param>
        Task NotifyConsumed(TimeSpan duration, string consumerType);

        /// <summary>
        /// Notify that a fault occurred during message consumption
        /// </summary>
        /// <param name="duration"></param>
        /// <param name="consumerType"></param>
        /// <param name="exception"></param>
        Task NotifyFaulted(TimeSpan duration, string consumerType, Exception exception);
    }
}

这是我需要在拦截中掌握的通用消息。

我可以成功地将它投射到一个对象上说:

ConsumeContext<AuthenticationDataRequest>

在 Visual Studio 中,一旦我投射了它,Message 对象就会弹出(没有投射就没有 MessageObject)。

要转换,我使用以下通用方法:

public Guid? RunMessageRetrieve(dynamic obj, Type castTo)
{
    MethodInfo castMethod = GetType().GetMethod("GetMessageIdFromContext").MakeGenericMethod(castTo);
    return castMethod.Invoke(null, new object[] { obj }) as Guid?;
}

public static Guid? GetMessageIdFromContext<T>(dynamic context) where T : class
{
    Guid? messageId = null; 

    try
    {
        var contextCasted = (T)context;
        Type contextType = contextCasted.GetType();
        var message = contextCasted.GetType().GetProperty("Message");
        if (message != null)
        {
            messageId = message.GetType().GetProperty("MessageId").GetValue(message) as Guid?;
        }
    }
    catch (InvalidCastException castException)
    {
        Console.WriteLine("Could not retrieve message Id from context message as the cast failed");
    }
    catch (NullException nullException)
    {
        Console.WriteLine("Could not retrieve message Id from context as the message Id did not exist");
    }

    return messageId;
}

在这里您可以在 Visual Studio 中看到消息,在其中我可以获取消息 ID:

但是,我尝试使用反射来获取实际的消息属性,因为我当然不知道编译时的类型,而且我似乎无法解决它。以下为空,因为它当然是泛型类型:

var message = contextCasted.GetType().GetProperty("Message");

这必须是可行的,因为在拦截后调用实际方法时,它具有带有消息的正确对象。

【问题讨论】:

    标签: c# generics casting castle-dynamicproxy


    【解决方案1】:

    只需转换为具有所需属性的接口,然后使用它。不需要反射。

    public static Guid? GetMessageId<T>(ConsumeContext<T> input) where T : class
    {
        var casted = input as ConsumeContext<IMessage>;
        if (casted == null) return null;
        return casted.Message.MessageId;
    }
    

    示例程序:

    public class Program
    {
        public static Guid? GetMessageId<T>(ConsumeContext<T> input) where T : class
        {
            var casted = input as ConsumeContext<IMessage>;
            if (casted == null) return null;
            return casted.Message.MessageId;
        }
        public static void Main()
        {
            var exampleObject = new MyClass<Message>();
            exampleObject.Message = new Message { MessageId = new Guid("00000001-0002-0003-0004-000000000005") };
    
            var messageId = GetMessageId(exampleObject);
            Console.WriteLine(messageId.Value);
        }
    }
    

    输出:

    00000001-0002-0003-0004-000000000005
    

    如果您必须向下转换,它也可以工作,即即使T 是特定类型的消息,您仍然可以将其转换为公开消息的接口或基类。

        public static void Main()
        {
            var exampleObject = new MyClass<SpecificMessage>();
            exampleObject.Message = new SpecificMessage { MessageId = new Guid("00000001-0002-0003-0004-000000000005") };
    
            var messageId = GetMessageId(exampleObject);
            Console.WriteLine(messageId.Value);
        }
    

    Full working example on DotNetFiddle

    【讨论】:

    • 不幸的是,由于 IMessage,这不起作用,消息是在我们不想覆盖的外部库 (MassTransit) 中定义的。不过谢谢你的建议。
    • 你不必使用接口;您还可以强制转换为 Message 类本身,假设您的所有消息类型都继承自它,并且它具有您需要的属性。
    • 消息是通用的 T Message { get; }
    【解决方案2】:

    我最后选择了以下,工作了,我没有想到立即像那样直接使用 Type。是动态参数搞砸了:

    public static Guid? GetMessageIdFromContext(object context, Type contextType) 
        {
            Guid? messageId = null;
    
            try
            {
                var contextProp = contextType.GetProperty("Message");
                if (contextProp != null)
                {
                    var message = contextProp.GetValue(context);
                    if (message != null)
                    {
                        messageId = message.GetType().GetProperty("UniqueId").GetValue(message) as Guid?;
                    }
    
                }
            }
            catch (NullException nullException)
            {
                Console.WriteLine("Could not retrieve message Id from context as the message Id did not exist");
            }
    
            return messageId;
        }
    

    【讨论】:

      猜你喜欢
      • 2019-10-15
      • 1970-01-01
      • 2015-02-20
      • 1970-01-01
      • 2015-08-16
      • 1970-01-01
      • 2012-05-13
      • 2021-10-17
      • 1970-01-01
      相关资源
      最近更新 更多