在TPL Dataflow中,是否可以在创建块之后但使用它之前更改DataflowBlockOptions? [英] In TPL Dataflow, is it possible to change DataflowBlockOptions after block is created but before it is used?

查看:28
本文介绍了在TPL Dataflow中,是否可以在创建块之后但使用它之前更改DataflowBlockOptions?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

...并且生效吗?

我想推迟设置ExecutionDataflowBlockOptions.SingleProducerConstrained属性,直到准备好将网络链接在一起为止.(因为,我想将创建具有其语义的块与将网络及其语义链接到一起分开.)

I'd like to defer setting the ExecutionDataflowBlockOptions.SingleProducerConstrained property until I'm ready to link the network together. (Because, I want to separate creating the blocks, with their semantics, from linking the network together, with its semantics.)

但据我所知,您只能在创建块时设置ExecutionDataflowBlockOptions(例如,对于TransformBlock,TransformManyBlock等,您将其传递给构造函数,否则将不可见).

But as far as I can tell you can only set the ExecutionDataflowBlockOptions when the block is created (e.g., for TransformBlock, TransformManyBlock, etc, you pass it in to the constructor and it is not visible otherwise).

但是...并没有逃避我的注意,这些属性具有公共设置器.所以...我可以用ExecutionDataflowBlockOptions的占位符实例创建该块并保持住该块,以便以后在需要时将块链接在一起时可以设置SingleProducerConstrained = true(这样它将生效)吗?

However ... it hasn't escaped my notice that the properties have public setters. So ... can I create the block with a placeholder instance of ExecutionDataflowBlockOptions and hold on to it so that I can later set SingleProducerConstrained=true if I desire, when linking the blocks together (and that it will take effect)?

(顺便说一句,除了测量吞吐量外,是否有其他方法可以判断SingleProducerConstrained是否有其他效果?)

(BTW, is there any way to tell if SingleProducerConstrained is having any effect other than measuring throughput?)

更新:@ i3amon在他的答案中正确指出了此操作无法完成,因为数据流块克隆了您传入并使用的 DataflowBlockOptions .但是无论如何,我还是通过使用内部数据结构来做到这一点的,我可以通过反射和动态访问它们.我将其放在下面的答案中.

Update: @i3amon correctly pointed out in his answer this can't be done because dataflow blocks clone the DataflowBlockOptions you pass in and use that. But I did it anyway, using internal data structures I can access via reflection and dynamic. I put that in an answer below.

推荐答案

让我回答我自己的问题.使用DotNetInside的Dataflow程序集反编译中的信息,例如 TransformBlock 此处的代码复合体(我在

Let me answer my own question. Using information from DotNetInside's decompile of the Dataflow assembly, for example, TransformBlock here (thanks @i3amon again for the link to dotnetinside.com), and the very nice ExposedObject package at codeplex here (which I learned about at this blog post, I did the following:

  • TPL数据流通过 DebuggerTypeProxy 属性阻止所有实现调试器可视化的工具,该属性应用于一种类型时,只要要使用原始类型,它就会在Visual Studio调试器中命名另一种类型.显示(例如,观看窗口).

  • The TPL Dataflow blocks all implement debugger visualizers via the DebuggerTypeProxy attribute, which, applied to a type, names another type to use in the Visual Studio debugger whenever the original type is to be displayed (e.g., watch window).

所有这些 DebuggerTypeProxy 命名类都是属性附加到的数据流块的内部类,通常命名为 DebugView .该类始终是私有的并且是密封的.它展示了有关数据流块的许多有趣内容,包括其真正的(不是副本) DataflowBlockOptions ,以及-如果该块是源块,则是- ITargetBlock [] ,可以用来在构建后从其起始块跟踪数据流网络.

Each of these DebuggerTypeProxy-named classes are inner classes of the dataflow block the attribute is attached to, usually named DebugView. That class is always private and sealed. It exposes lots of cool stuff about the dataflow block, including its genuine (not a copy) DataflowBlockOptions and also - if the block is a source block - an ITargetBlock[], which can be used to trace the dataflow network from its start block after construction.

一旦获得 DebugView 的实例,就可以通过 ExposedObject 使用 dynamic 来获取由类- ExposedObject 使您可以获取一个对象,并使用普通的方法和属性语法访问其方法和属性.

Once you get an instance of the DebugView you can use dynamic via ExposedObject to get any of the properties exposed by the class - ExposedObject lets you take an object and use ordinary method and property syntax to access its methods and properties.

因此,您可以从数据流块中获取 DataflowBlockOptions 并更改其 NameFormat ,如果它是 ExecutionDataflowBlockOptions (并且尚未将块连接到其他块),可以更改其 SingleProducerConstrained 值.

Thus you can get the DataflowBlockOptions out of the dataflow block and change its NameFormat, and if it is an ExecutionDataflowBlockOptions (and you haven't yet hooked up the block to other blocks) you can change its SingleProducerConstrained value.

但是,您不能使用 dynamic 查找或构造内部 DebugView 类的实例.您需要对此进行反思.首先,将 DebuggerTypeProxy 属性从您的数据流块的类型,获取调试类的名称,假定它是的内部类数据流块的类型并进行搜索,将其转换为封闭的通用类型,最后构造一个实例.

However you can't use dynamic to find or construct the instance of the inner DebugView class. You need reflection for that. You start by getting the DebuggerTypeProxy attribute off your dataflow block's type, fetch the name of the debugging class, assume it is an inner class of the dataflow block's type and search for it, convert it to a closed generic type, and finally construct an instance.

请充分意识到您正在使用数据流内部的未记录代码.用你自己的判断这是否是一个好主意.在我看来,TPL Dataflow的开发人员做了很多工作来支持在调试器中查看这些块,他们可能会继续努力.详细信息可能会更改,但是,如果您对这些类型的反射和动态使用进行了适当的错误检查,则可以发现代码何时停止使用新版本的TPL Dataflow.

Be fully aware that you're using undocumented code from the dataflow internals. Use your own judgement about whether this is a good idea. In my opinion, the developers of TPL Dataflow did a lot of work to support viewing these blocks in the debugger, and they'll probably keep it up. Details may change, but, if you're doing proper error checking on your reflection and dynamic use of these types, you will be able to discover when your code stops working with a new version of TPL Dataflow.

以下代码片段可能无法一起编译-它们只是从我的工作代码中剪切和粘贴而来,来自不同的类,但是它们确实可以为您提供帮助.我做的很好.(此外,为简洁起见,我消除了所有错误检查.)(而且,我仅使用TPL数据流4.5.20.0版开发/测试了此代码,因此您可能必须将其适应于过去(或将来!)的版本.)/p>

The following code fragments probably don't compile together - they're simply cut&pasted out of my working code, from different classes, but they certainly give you the idea. I made it work fine. (Also, for brevity, I elided all error checking.) (Also, I developed/tested this code with version 4.5.20.0 only of TPL dataflow, so you may have to adapt it for past - or future! - versions.)

// Set (change) the NameFormat of a dataflow block after construction
public void SetNameFormat(IDataflowBlock block, string nameFormat)
{
    try
    {
        dynamic debugView = block.GetInternalData(Logger);
        if (null != debugView)
        {
            var blockOptions = debugView.DataflowBlockOptions as DataflowBlockOptions;
            blockOptions.NameFormat = nameFormat;
        }
    }
    catch (Exception ex)
    {
        ...
    }
}

// Get access to the internal data of a dataflow block via its DebugTypeProxy class
public static dynamic GetInternalData(this IDataflowBlock block)
{
    Type blockType = block.GetType();
    try
    {
        // Get the DebuggerTypeProxy attribute, which names the debug class type.
        DebuggerTypeProxyAttribute debuggerTypeProxyAttr =
            blockType.GetCustomAttributes(true).OfType<DebuggerTypeProxyAttribute>().Single();

        // Get the name of the debug class type
        string debuggerTypeProxyNestedClassName =
            GetNestedTypeNameFromTypeProxyName(debuggerTypeProxyAttr.ProxyTypeName);

        // Get the actual Type of the nested class type (it will be open generic)
        Type openDebuggerTypeProxyNestedClass = blockType.GetNestedType(
            debuggerTypeProxyNestedClassName,
            System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);

        // Close it with the actual type arguments from the outer (dataflow block) Type.
        Type debuggerTypeProxyNestedClass =
            openDebuggerTypeProxyNestedClass.CloseNestedTypeOfClosedGeneric(blockType);

        // Now create an instance of the debug class directed at the given dataflow block.
        dynamic debugView = ExposedObject.New(debuggerTypeProxyNestedClass, block);

        return debugView;
    }
    catch (Exception ex)
    {
        ...
        return null;
    }
}

// Given a (Type of a) (open) inner class of a generic class, return the (Type
// of the) closed inner class.
public static Type CloseNestedTypeOfClosedGeneric(
                       this Type openNestedType,
                       Type closedOuterGenericType)
{
    Type[] outerGenericTypeArguments = closedOuterGenericType.GetGenericArguments();
    Type closedNestedType = openNestedType.MakeGenericType(outerGenericTypeArguments);
    return closedNestedType;
}

// A cheesy helper to pull a type name for a nested type out of a full assembly name.
private static string GetNestedTypeNameFromTypeProxyName(string value)
{
    // Expecting it to have the following form: full assembly name, e.g.,
    // "System.Threading...FooBlock`1+NESTEDNAMEHERE, System..."
    Match m = Regex.Match(value, @"^.*`\d+[+]([_\w-[0-9]][_\w]+),.*$", RegexOptions.IgnoreCase);
    if (!m.Success)
        return null;
    else
        return m.Groups[1].Value;
}
// Added to IgorO.ExposedObjectProject.ExposedObject class to let me construct an 
// object using a constructor with an argument.
public ExposedObject {
    ...

    public static dynamic New(Type type, object arg)
    {
        return new ExposedObject(Create(type, arg));
    }

    private static object Create(Type type, object arg)
    {
        // Create instance using Activator
        object res = Activator.CreateInstance(type, arg);
        return res;

        // ... or, alternatively, this works using reflection, your choice:
        Type argType = arg.GetType();
        ConstructorInfo constructorInfo = GetConstructorInfo(type, argType);
        return constructorInfo.Invoke(new object[] { arg });
    }
    ...
}

这篇关于在TPL Dataflow中,是否可以在创建块之后但使用它之前更改DataflowBlockOptions?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆