任何方式来返回一个不存在但稍后将从C#方法中的对象? [英] Any way to return an object which does not exist yet but will later from a C# method?

查看:100
本文介绍了任何方式来返回一个不存在但稍后将从C#方法中的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明1:疯狂的迂腐语言弯曲前进。



免责声明2:对于现在或未来的任何客户 - 我不会为此收费。



好的,所以这并不是必须的,但我正在为xunit.net创建插件,并且出现了一个有趣的场景



目前,

  [规格] 
void calculator_addition() {
Calculator calc = null;
User user = null;
给定一个计算器和一个用户.Context(()=> {
calc = new Calculator();
user = new User(calculateQuota:100);
}); $()=> result = calc.Add(1,1));
added 1 + 1。
等于2.Assert((=)=> calc.Result.ShouldEqual(2));
将使用户的配额减少一个。
}

这不是我认识并喜欢的美丽,优雅的C#像声明未初始化的变量一样,我宁愿不声明它们。我更愿意做这样的事情:

  [规范] 
void calculator_addition(){
()=> new {
Calc = new Calculator(),
User = new User(calculationQuota:100),
});
added 1 + 1.Do(()=> _ Calc.Add(1,1));
将等于2.Assert((=)=> _ Calc.Result.ShouldEqual(2));
会将用户配额减少一个。插入(()=> _。User.CalculationsLeft.ShouldEqual(99));



$ b

在这种情况下,Context()扩展方法的签名是 Context Context(此字符串,Action setUpWith)将具有签名 T Context< T>(this string,Func< T> setUpWith)。在实现中出现了一个有趣的问题,但由于setUpWith委托在这个时候并未实际执行,它只是被存储并随后被自定义的Specification属性执行。这意味着目前还没有真正的T回归。但是,由于存储的委托是按顺序执行的,所以我可以保证它在Do()方法中的委托被调用时存在。



最好是为T返回一个动态代理,但这并不可行,因为我希望能够做的事情之一是使用匿名类型,这会使T被封。

现在在C ++版本中,我认为可以将内存分配给对象,并将对象作为引用返回到空间位置,最终填补。这实质上是如果我将T作为ref参数传递的话会发生什么(但是之后我不得不声明它会破坏该点)。

仍然有角落的C#,我还没有探索。任何人都可以想出一种方法来挤出我想要的语法吗?



PS。我有一些解决方案,稍微修改语法,我将在后面以答案的形式发布。

解决方案

肯定有办法设计API的方式是,您不必使用未初始化的变量并在lambda表达式中设置它们的值(事实上,函数式编程可以非常容易地在没有突变的情况下生存)。



我并不是那么熟悉你所谈论的API,但是如果你修改了API来大致看起来像这样,那么我认为它应该很好地工作而不会发生突变:

 给定一个计算器和一个用户
//构造并返回将保存状态的匿名对象
//('Context'需要()=> new {Calc = new Calculator();
User = new User(calculateQuota:100)})$ b $('Func< T& b。然后(ctx => {
adding 1 + 1.Do(()=> result = ctx.Calc.Add(1,1));
将等于2。置位(()=> ctx.Calc.Result.ShouldEqual(2));
将使用户的配额减少一个。(()=>
ctx.User.CalculationsLeft.ShouldEqual(99));
});

通过包装在上下文初始化为另一个lambda表达式后应该运行的主体,您应该能够避免初始化问题。 然后方法将简单地将lambda函数存储在某个地方(并且在初始化状态后用返回的上下文调用它)。


Disclaimer 1: Crazy pedantic language-bending drivel ahead.

Disclaimer 2: To any clients present or future - I am not billing you for this.

Alright, so this is not really necessary but I'm messing around with creating plugins for xunit.net and an interesting scenario comes up

Currently, the example of the SubSpec extension that ships with the source works something like this:

[Specification]
void calculator_addition() {
  Calculator calc = null;
  User user = null;
  "Given a calculator and a user".Context(()=> {
       calc = new Calculator();
       user = new User(calculationQuota: 100);
   });
  "adding 1 + 1".Do(()=>result = calc.Add(1, 1));
  "will equal 2".Assert(()=>calc.Result.ShouldEqual(2));
  "will decrease user's quota by one".Assert(()=>user.CalculationsLeft.ShouldEqual(99));
}

That's not the beautiful, elegant C# that I know and love - I don't like declaring uninitialized variables and I prefer not to declare them at all. I would much prefer to do something like this:

[Specification]
void calculator_addition() {
  var _ =
  "Given a calculator and a user".Context(()=> new {
       Calc = new Calculator(),
       User = new User(calculationQuota: 100),
   });
  "adding 1 + 1".Do(()=>_.Calc.Add(1, 1));
  "will equal 2".Assert(()=>_.Calc.Result.ShouldEqual(2));
  "will decrease user's quota by one".Assert(()=>_.User.CalculationsLeft.ShouldEqual(99));
}

In this case, the Context() extension method would have the signature void Context(this string, Action setUpWith) would have the signature T Context<T>(this string, Func<T> setUpWith). An interesting problem arises in implementation though since the setUpWith delegate isn't actually executed at this time, it is merely stored and then executed later by the custom Specification attribute. This means that there is not really a T to return at this time. However, since the stored delegates are executed in order, I can guarantee that it will exist by the time the delegate in the Do() method is called.

So what I would be nice is to return a dynamic proxy for T, but this is not really feasible since one of the things I'd like to be able to do is use anonymous types which would make T sealed.

Now in C++ land I believe that it would be possible to allocate memory to the object and return the "object" as a reference to that bit of space which will then get filled in eventually. This is essentially what would happen if I passed T in as a ref parameter (but then I'd have to declare it defeating the point).

Still, there are corners of C# that I have not explored. Can anyone think of a way to squeeze my desired syntax out of this?

PS. I have a couple solutions with slight modifications to the syntax which I will post as answers below.

解决方案

There is surely a way to design the API in a way such that you don't have to use uninitialized variables and set their values in a lambda (in fact, functional programming can live without mutation quite easily).

I'm not all that familiar with the API you're talking about, but if you modified the API to look roughly like this, then I think it should work nicely without mutation:

"Given a calculator and a user"
   // Constructs and returns anonymous object that will hold the state
   // ('Context' takes 'Func<T>' and returns some wrapper)
   .Context(() => new { Calc = new Calculator(); 
                        User = new User(calculationQuota: 100) })
   .Then(ctx => {
      "adding 1 + 1".Do(() => result = ctx.Calc.Add(1, 1)); 
      "will equal 2".Assert(() => ctx.Calc.Result.ShouldEqual(2)); 
      "will decrease user's quota by one".Assert(() => 
         ctx.User.CalculationsLeft.ShouldEqual(99)); 
   });

By wrapping the body that should be run after the context is initialized into another lambda expression, you should be able to avoid the problem with initialization. The Then method would simply store the lambda function somewhere (and call it with the returned context once the state is initialized).

这篇关于任何方式来返回一个不存在但稍后将从C#方法中的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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