Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#==或==中类似宏的结构如何分解包含';返回';_C#_Macros_Return - Fatal编程技术网

C#==或==中类似宏的结构如何分解包含';返回';

C#==或==中类似宏的结构如何分解包含';返回';,c#,macros,return,C#,Macros,Return,我从来没有想过我需要这样的东西(因为宏是坏的,对吧?),但事实证明我需要 我发现自己在代码中一遍又一遍地重复以下模式,几十次甚至几百次: object Function() { this.Blah = this.Blah.Resolve(...); if(this.Blah == null) return null; if(this.Blah.SomeFlag) return this.OtherFunction(); // ot

我从来没有想过我需要这样的东西(因为宏是坏的,对吧?),但事实证明我需要

我发现自己在代码中一遍又一遍地重复以下模式,几十次甚至几百次:

object Function() {
    this.Blah = this.Blah.Resolve(...);
    if(this.Blah == null)
        return null;
    if(this.Blah.SomeFlag)
        return this.OtherFunction();

    // otherwise, continue processing...
}
我想写一些类似的东西:

object Function() {
    this.Blah = this.Blah.Resolve(...);
    VerifyResolve(this.Blah);

    // continue processing...
}
除了模式包含条件
return
s这一事实,这意味着我无法计算出函数。我已经有一次微妙地改变了模式,因为我无法进行简单的搜索,所以很难找到所有的实例


如何避免不必要的重复,并使将来对该模式的任何更改更容易?

最好使用VerifyResolve extantion方法来extand Blah类。
如果您拥有Blah源代码,那么最好创建一些可转换的接口并让Blah实现它。

您可以这样说:

object Function() { 
    this.Blah = this.Blah.Resolve(...);
    object result;
    if (VerifyResolve(this.Blah, out result))
        return result;

    // continue processing... 
} 
object Function()
{ 
    this.Blah = this.Blah.Resolve(...);
    object rc;
    if (!VerifyResolve(out rc)
       return rc;

    // continue processing... 
} 

bool VerifyResolve(out object rc)
{
    if(this.Blah == null)      
    {
        rc = null;
        return true;
    }
    if(this.Blah.SomeFlag)      
    {
        rc = this.OtherFunction();
        return true;
    }
    rc = null;
    return false;
}

如果您经常发现必须检查
null
,则可能需要使用。本质上,您创建了一个表示空结果的对象。然后可以返回此对象,而不是
null
。然后,您必须调整代码的其余部分,以包括null对象的行为。大概是这样的:

MyClass Function() 
{  
  this.Blah = this.Blah.Resolve(...); 
  VerifyResolve(this.Blah);

  // continue processing...  
}  

MyClass VerifyResolve(MyClass rc) 
{
  // ...
  return rc.Blah.SomeFlag ? rc.OtherFunction() : rc;
} 

NullMyClass : MyClass {
  public override bool SomeFlag { get { return false; } }
}
也许更整洁:

if (!TryResolve(this.Blah)) return this.Blah;

其中TryResolve将this.Blah的值设置为null或this.OtherFunction,并返回一个适当的bool

没有冒犯,但其中有一些代码味道:

this.Blah = this.Blah.Resolve(...);
这句话尤其会让我开始重新思考

我遇到的主要问题是对象返回类型以及将属性赋值给通过调用该属性上的方法返回的值。这两个系统闻起来都像是你在某个地方把继承搞砸了,最终得到了一个非常有状态的系统,这既是一个需要测试的bug,也是一个需要维护的痛苦

也许最好是重新思考,而不是试图使用黑客和技巧来回避这个问题:我通常发现,如果我试图滥用语言的一个功能,如宏,那么我的设计需要工作

编辑

好的,添加信息后,也许这不是什么味道,但我还是建议如下:

class ExampleExpr{
    StatefulData data ... // some variables that contain the state data
    BladhyBlah Blah { get; set; }

    object Function(params) {
        this.Blah = this.Blah.Resolve(params);
        ....
    }
}
这段代码令人担忧,因为它强制采用完全基于状态的方法,输出取决于之前发生的情况,因此需要特定的步骤进行复制。这是一个痛苦的考验。另外,如果调用函数()两次,如果不知道Blah最初处于什么状态,就无法保证它会发生什么

class ExampleExpr{
    StatefulData data ... // some variables that contain the state data

    object Function(params) {
        BlahdyBlah blah = BlahdyBlah.Resolve(params, statefulData);
    }
}
如果我们改用工厂风格的方法,每当我们看到一组特定的参数时就返回一个带有特定信息的新实例,那么我们就消除了一个使用有状态数据的地方(即,现在在每次调用时都使用特定的参数集重新创建BladhyBlah实例)

这意味着我们可以通过使用特定的Setup()调用函数(params)来复制测试中的任何功能,以创建statefulData和一组特定的params

理论上,这效率较低(因为每次工厂调用都会创建一个新的BlahdyBlah),但是可以使用特定数据缓存BlahdyBlah实例,并在工厂调用之间共享它们(假设它们没有影响其内部状态的其他方法)。然而,它更易于维护,并且从测试的角度来看,它完全消除了有状态数据的缺陷

这也有助于消除您最初的问题,因为当我们不依赖内部实例变量时,我们都可以从函数(params)外部解析(params,statefulData),如果blah==null或blah.SomeFlag==SomeFlag.Whatever,则不调用函数(params)。因此,通过将其移出方法,我们不再需要担心返回

希望这是在正确的范围内,举一个小例子,很难确切地知道该推荐什么,因为这里通常有困难/抽象的问题


这是混乱的,恼人的,冗长的,但这是我不得不去的。可能没有更好的办法了

另一种方法是生成一个结构,它的可空性非常重要

struct Rc
{
  internal object object;
  internal Rc(object object) { this.object = object; }
}

object Function() 
{  
    this.Blah = this.Blah.Resolve(...); 
    Rc? rc = VerifyResolve();
    if (rc.HasValue)
       return rc.Value.object; 

    // continue processing...  
}  

Rc? VerifyResolve() 
{ 
    if(this.Blah == null)       
    { 
        return new Rc(null); 
    } 
    if(this.Blah.SomeFlag)       
    { 
        return new Rc(this.OtherFunction()); 
    } 
    return null; 
} 

您不需要将这个.Blah作为ref或out参数传递,否则您只需要设置传递到TryResolve的方法参数的引用,而不是这个.Blah本身…非常好,只是我不想在返回时覆盖这个.Blah。但是,+1,因为你追求简单。我讨厌这20行C#解决方案,它们在其他语言中只有2行或3行……这是个好主意,但在这种情况下,
null
可以安全使用,并且是
Resolve
设计的一部分。有趣的想法。。。这是我正在做的。我正在编写一个编译器,并解析表达式。每个表达式要么返回自身,要么返回要替换自身的表达式(出于性能原因,在出现错误时返回null,以避免异常)。例如,对
IdentifierExpr
调用
Resolve
可能会导致
SymbolExpr
引用变量。你认为这个设计是代码嗅觉吗?同样,实际的返回类型不是<代码>对象< /代码>,我只是为了代码的目的而使用它。真正的返回类型是
Expr
。(如果可能的话,我总是避免像瘟疫一样使用
object
)@zildjohn01无论是用于演示目的的obj还是用于错误处理的null返回都是有意义的(可能应该在Q中提到这一点,但我不适合外推)。我仍然会考虑使用X.= .X.BLAH有点奇怪,就像手动修改对象访问者风格的状态一样。也许从设计的角度来看,使用某种静态包装器,通过分配局部变量来移除重新分配会更好?我不愿意在不了解代码其余部分的情况下提出任何具体的改进建议,但一般来说,副作用=>不好