考虑以下C#代码:
public static Foo CreateFoo()
{
return new Foo();
}
// ...
public static void Main()
{
IFoo foo = CreateFoo();
}
相当简单:赋值语句允许目标变量的类型不如表达式的类型。
所以我希望注释掉的行能够正常工作,但由于下面列出的原因而失败,强制任何设置TheFoo
的东西都返回具体Foo
类的实例,而不仅仅是{ {1}}:
IFoo
public class ActivityWithOutArgument : CodeActivity<Foo>
{
protected override Foo Execute(CodeActivityContext context)
{
return new Foo();
}
}
当我处理返回一个特别令人讨厌的具体类的遗留代码时出现了这种情况,这在我们的测试套件中工作相当令人讨厌(例如,所有创建此bugger实例的测试都在我们的构建服务器上失败但是在本地传递,并解决这个问题是非常重要的。)
我们已经有一些<Activity x:Class="MyNamespace.TheActivity"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mine="clr-namespace:MyNamespace">
<Sequence>
<Sequence.Variables>
<!--
The following fails on the "mine:ActivityWithOutArgument" line, with:
Compiler error(s) encountered processing expression "TheFoo".
Option Strict On disallows implicit conversions from 'MyNamespace.IFoo' to 'MyNamespace.Foo'.
<Variable x:TypeArguments="mine:IFoo" Name="TheFoo" />
-->
<!-- This works -->
<Variable x:TypeArguments="mine:Foo" Name="TheFoo" />
</Sequence.Variables>
<mine:ActivityWithOutArgument Result="[TheFoo]" />
</Sequence>
</Activity>
的现有活动(并且它们的所有测试都在构建服务器上失败,并且调用了一个非常讨厌的静态方法)。因此,当我负责创建内部使用的对应活动时,我想&#34;好的,所以让新活动使用OutArgument<Foo>
,以便我可以使用模拟&#34;来测试它。
因此调用活动内部的逻辑看起来像(使用C#作为类比):
OutArgument<IFoo>
我想有一种解决方法可以让它看起来像这样:
public static void Run()
{
IFoo foo = TryExistingActivity();
if (foo == null)
{
foo = UseMyShinyNewActivity();
}
}
......这可能就是我最终要做的事情,尽管我倾向于只是遵循传统的东西并使用shims, the most cheating thing I've ever seen,作为一个魔杖使单元测试不会失败。
从根本上说,为什么我不能使用public static void Run()
{
IFoo foo;
Foo uglyWorkaround = TryExistingActivity();
if (uglyWorkaround == null)
{
foo = UseMyShinyNewActivity();
}
else
{
foo = uglyWorkaround;
}
}
存储活动结果Variable<IFoo>
?或者这只是标准&#34;实施成本与效益&#34;事?
这使用来自&#34;子活动定义&#34;的相同活动。上方。
OutArgument<Foo>
答案 0 :(得分:0)
由于WF的设计方式,输入和输出由泛型类表示,这些类不能是协变的或逆变的。
然而,WF没有什么根本可以防止这种情况,所以这有效:
using System;
using System.Activities;
using System.Activities.Statements;
using System.Activities.Expressions;
namespace WorkflowConsoleApplication1
{
public interface IFoo
{
void DoThing();
}
public sealed class Foo : IFoo
{
public void DoThing() => Console.WriteLine("Hello");
}
public sealed class ActivityWithOutArgument : CodeActivity<Foo>
{
protected override Foo Execute(CodeActivityContext context) => new Foo();
}
internal static class Program
{
private static void Main()
{
Variable<IFoo> theFoo = new Variable<IFoo>(nameof(theFoo));
Sequence sequence = new Sequence
{
Variables =
{
theFoo,
},
Activities =
{
new Assign<IFoo>
{
To = theFoo,
Value = new Cast<Foo, IFoo>
{
Operand = new ActivityWithOutArgument(),
},
},
new InvokeMethod
{
TargetObject = new InArgument<IFoo>(theFoo),
MethodName = nameof(IFoo.DoThing),
},
}
};
WorkflowInvoker.Invoke(sequence);
}
}
}