2012-10-12 143 views
0

如果在.NET中创建类,它将是引用类型。在这个例子中,我在SomeMethod中设置了MyClass的名字。现在在Main方法中,我不需要从SomeMethod中分配返回值,因为传入的参数是引用类型。我想在主要方法中的实例具有新的更新名称“约翰”,它的确如此。将返回值分配给.NET中的引用类型

为了便于阅读,我经常为受让人返回返回值,即使这不是必要的,以便将更新后的属性重新分配给main中的原始实例。我的假设是编译器足够聪明,不会在Main方法内重新创建MyClass的新引用。真的吗?或者编译器实际上是否创建了一个新的引用,并在为自己分配返回值时指向更新后的值?

//My class will be a reference type 
public class MyClass 
{ 
    public int ID { get; set; } 
    public string name { get; set; } 
} 

public class Main 
{ 
    //Main method of application 
    public void Main() 
    { 
     MyClass myClass = new MyClass(); 
     //return value back to itself 
     myClass = SomeMethod(myClass); 
    } 

    public MyClass SomeMethod(MyClass myClass) 
    { 
     myClass.name = "John"; 
     return myClass; 
    } 
} 

这里是IL

instance void Main1() cil managed 
{ 
    // Method begins at RVA 0x207c 
    // Code size 15 (0xf) 
    .maxstack 2 
    .locals init (
     [0] class Test.MyClass myClass 
    ) 

    IL_0000: newobj instance void Test.MyClass::.ctor() 
    IL_0005: stloc.0 
    IL_0006: ldarg.0 
    IL_0007: ldloc.0 
    IL_0008: call instance class Test.MyClass Test.Main::SomeMethod(class Test.MyClass) 
    IL_000d: stloc.0 
    IL_000e: ret 
} // end of method Main::Main1 

回答

1

要回答你的问题,是的编译器足够聪明,不会将返回值重新分配给同一个对象。如果我们打开与反射器或dotPeek,你的代码代码:

public void Main() 
{ 
    MyClass myClass = new MyClass(); 
    //return value back to itself 
    myClass = SomeMethod(myClass); 
} 

仅仅是变换到:

public void Main() 
{ 
    this.SomeMethod(new MyClass()); 
} 

即使我们做了重新分配变量的东西,它仍然是由编译器中删除。例如,编译器转换这样的:

public void Main() 
{ 
    MyClass myClass = new MyClass(); 
    //return value back to itself 
    myClass = SomeMethod(myClass); 

    Console.WriteLine(myClass.name); 
} 

到:

public void Main() 
{ 
    Console.WriteLine(this.SomeMethod(new MyClass()).name); 
} 

但是,像L CWS个Therin和罗伯特·杰普森说,你不应该这样做,因为这是不好的做法,并可能导致误解

+0

你确定它变成了“this.SomeMethod”吗?考虑到Main是一种静态方法?虽然我注意到你省略了“静态”关键字(在这种情况下,你的答案可能是正确的)。 –

+0

你是对的,我创建了主类。但编译器使用静态方法(没有“this”)做同样的事情。而OP也没有静态方法 –

+0

是的,看起来你是正确的。我在原帖中添加了IL。 – Thomas

1

类被作为参考方法过去了,所以你要修改的同样的事情,你回来了。

当一个基于引用类型的对象被传递给一个方法时,没有 该对象的副本被创建。相反,对作为方法参数的对象 的引用被创建并传递。因此通过 所做的更改将在调用方法中反映出来。

来源:http://msdn.microsoft.com/en-us/library/ms173114(v=vs.80).aspx

关于可读性:你的方法应该永远是不言自明的。在我看来,方法的名称应该说明它的作用。

void AddClientContextToContainer(Container destination) 
+0

我想你错过了我的问题。我明白这个类本身没有通过,它是我提到的一个引用类型。我问是否在主方法中通过赋值返回值,编译器是否在主方法中创建了另一个“引用”。 – Thomas

+0

'类是通过引用方法传递的。不,他们不是。类*是*引用,它们通过* value *(除非使用ref/out)传递给方法。被复制的值只是一个参考。 – Servy

+0

@Servy你说得对。他们通过**作为参考,而不是通过参考。 –

2

它不创建新的参考。

但是,它没有任何意义。这应该足够了:

public void SomeMethod(MyClass myClass) 
{ 
    myClass.name = "John"; 

} 

以上是自然的,实际上比您建议的方式更不复杂。

+0

我了解它不需要。正如我所说,它似乎更易于我阅读。 – Thomas

+0

真的吗?我不同意。我想每个人都有自己的想法。 –

+5

@Thomas返回修改后的实例向消费者指出这是一个新实例,并且传入的实例将不会受到伤害,显然不是这种情况。有害! –

1

在你的情况下,你正在创建一个实例的对象。实际实例通过new创建。参考本质上只是一个整数(32位系统上的32位,64位系统上的64位)。该整数指向对象实例在内存中的位置。 “创建一个新的参考”本质上与创建一个新整数的工作量相同(意思是非常非常小;它是关于计算机可以执行的最小可能工作单位)。

void替代方案相反,您的代码将执行两个(冗余)引用副本。 (一次将方法参数复制到堆栈中的返回值,然后再从该返回值返回到原始变量。)

实际的额外工作量很小。它存在,但它确实很小。这里的关注点应该只是可读性。如果你发现代码更易于阅读和理解,那么很好。请注意,这是一种通常只在MyClass是不可变的情况下使用的模式,其中返回值是对新实例的引用,而不是传入的实例。这可能会导致其他人阅读您的代码时感到困惑。如果他们错误地认为该对象是不可变的,它可能会导致......不好的事情。

其他人建议使该方法返回void,但我会说,它可能对类变得不可变是有意义的。你已经遵循了一个更加不可改变的模式。

+0

这是我关心的问题。是否在主要方法中创建了另一个参考,您声称它的确存在(您是否有参考,没有双关语意思)。乍一看,这似乎是自然而然的正确答案。我想知道编译器是否足够聪明,知道它不需要在这里创建另一个引用,因为在技术上没有必要。 – Thomas

+0

@Thomas将“参考”与“实例”区分开来非常重要。您正在执行“引用”的副本,但您并未创建或复制任何实际的“实例”。复制引用与复制单个整数相同;它是相同数量的工作(正如我所说的那样,无论它所引用的对象的大小如何,该数量都是**小**)。 – Servy

相关问题