我能否检测到我是否已将新对象作为参数?

精简版

对于那些没有时间阅读我对以下问题的推理的人:

有没有办法为方法的参数强制实施“仅新对象”或“仅现有对象”的策略

长版

有很多方法将对象作为参数,并且方法是否具有对象“all to their”并不重要。 例如:

var people = new List(); Person bob = new Person("Bob"); people.Add(bob); people.Add(new Person("Larry")); 

这里List.Add方法采用了“现有” Person (Bob)以及“new” Person (Larry),该列表包含两个项目。 Bob可以作为bobpeople[0] 。 拉里可以作为people[1]访问,如果需要,可以随后缓存和访问larry (或其他)。

好的。 但有时一个方法确实应该传递给新对象。 以Array.Sort为例。 以下内容并没有多大意义:

 Array.Sort(new int[] {5, 6, 3, 7, 2, 1}); 

所有上面的代码都是采用一个新数组,对它进行排序,然后忘记它(因为它的引用计数在Array.Sort退出后达到零,因此排序的数组将被垃圾收集,如果我没有记错的话) 。 因此Array.Sort 期望 “现有”数组作为其参数。

可以想象其他方法可能期望 “新”对象(尽管我通常认为这样的期望会是设计错误)。 一个不完美的例子是:

 DataTable firstTable = myDataSet.Tables["FirstTable"]; DataTable secondTable = myDataSet.Tables["SecondTable"]; firstTable.Rows.Add(secondTable.Rows[0]); 

正如我所说,这不是一个很好的例子,因为DataRowCollection.Add实际上并没有真正期望一个新的 DataRow ; 但它确实期望DataRow不属于DataTable 。 所以上面代码中的最后一行不起作用; 它需要是:

 firstTable.ImportRow(secondTable.Rows[0]); 

无论如何,这是我的问题的很多设置,它是: 有没有办法在方法的参数中强制执行“仅新对象”或“仅现有对象”的策略 ,或者通过某些自定义属性我不知道或者在方法本身内(也许是通过反思,虽然我可能会回避它,即使它可用)?

如果没有,任何有关如何实现这一目标的有趣想法都将受到欢迎。 例如,我想如果有某种方法来获取给定对象的GC引用计数,您可以立即告诉方法的开头是否已收到新对象(假设您正在处理引用类型)当然 – 这是唯一与此问题相关的场景。


编辑

版本越长越长。

好吧,假设我有一些方法可以选择接受TextWriter来输出它的进度或者你有什么:

 static void TryDoSomething(TextWriter output) { // do something... if (output != null) output.WriteLine("Did something..."); // do something else... if (output != null) output.WriteLine("Did something else..."); // etc. etc. if (output != null) // do I call output.Close() or not? } static void TryDoSomething() { TryDoSomething(null); } 

现在,让我们考虑两种不同的方法来调用此方法:

 string path = GetFilePath(); using (StreamWriter writer = new StreamWriter(path)) { TryDoSomething(writer); // do more things with writer } 

要么:

 TryDoSomething(new StreamWriter(path)); 

嗯……看起来这会造成问题,不是吗? 我构建了一个实现IDisposableStreamWriter ,但是TryDoSomething不会假设知道它是否具有对其output参数的独占访问权限。 因此,物体要么过早地放置(在第一种情况下),要么根本不放置(在第二种情况下)。

我不是说这必将是一个伟大的设计。 也许Josh Stodola是对的,这从一开始就是一个坏主意。 无论如何,我问这个问题主要是因为我只是好奇这样的事情是否可能 。 看起来答案是:不是真的。

不,基本上。

两者之间真的没什么区别:

 var x = new ...; Foo(x); 

 Foo(new ...); 

实际上有时您可能会在两者之间进行转换以进行调试。

请注意,在DataRow / DataTable示例中,有一种替代方法 – DataRow可以将其父级知道为其状态的一部分。 这与“新”或不是“新”不同 – 例如,你可以进行“分离”操作。 根据对象的真实硬性状态来定义条件比使用诸如“新”之类的粗俗术语更有意义。

是的,有办法做到这一点。

有点。

如果将参数设为ref参数,则必须将现有变量作为参数。 你做不到这样的事情:

 DoSomething(ref new Customer()); 

如果这样做,您将收到错误“ref或out参数必须是可分配变量”。

当然,使用ref还有其他含义。 但是,如果您是编写该方法的人,则无需担心它们。 只要你不在方法中重新分配ref参数,无论你是否使用ref都不会有任何区别。

我不是说这是好的风格,必然。 你不应该使用ref或out,除非你真的,真的需要并且没有其他办法去做你正在做的事情。 但是使用ref将使您想要做的工作。

不。如果有某些原因你需要这样做,你的代码就有不正确的架构。

简短的回答 – 没有

在绝大多数情况下,我通常会发现您上面列出的问题并不重要。 当他们这样做时,您可以重载方法,以便您可以接受其他内容作为参数而不是您担心共享的对象。

 // For example create a method that allows you to do this: people.Add("Larry"); // Instead of this: people.Add(new Person("Larry")); // The new method might look a little like this: public void Add(string name) { Person person = new Person(name); this.add(person); // This method could be private if neccessary } 

我可以想办法做到这一点,但我绝对不会推荐这个。 只是为了争论的缘故。

对象成为“新”对象意味着什么? 这意味着只有一个引用使它保持活着。 “现有”对象将具有多个引用。

考虑到这一点,请查看以下代码:

  class Program { static void Main(string[] args) { object o = new object(); Console.WriteLine(IsExistingObject(o)); Console.WriteLine(IsExistingObject(new object())); o.ToString(); // Just something to simulate further usage of o. If we didn't do this, in a release build, o would be collected by the GC.Collect call in IsExistingObject. (not in a Debug build) } public static bool IsExistingObject(object o) { var oRef = new WeakReference(o); #if DEBUG o = null; // In Debug, we need to set o to null. This is not necessary in a release build. #endif GC.Collect(); GC.WaitForPendingFinalizers(); return oRef.IsAlive; } } 

这在第一行显示True,在第二行显示False。 但是,请不要在您的代码中使用它。

让我把你的问题重写为更短的问题。

在我的方法中,以对象作为参数,有没有办法知道这个对象是否会在我的方法之外使用?

简而言之就是: 不。

让我在这一点上冒险发表意见:不应该有任何这样的机制。

这会使整个地方的方法调用复杂化。

如果有一种方法我可以在方法调用中告诉我给出的对象是否真的会被使用,那么作为该方法的开发者,这是一个信号,要考虑到这一点。

基本上,你会看到这种类型的代码(假设,因为它不可用/支持:)

 if (ReferenceCount(obj) == 1) return; // only reference is the one we have 

我的意见是:如果调用您的方法的代码不会将该对象用于任何事情,并且除了修改对象之外没有副作用,那么该代码不应该存在。

这就像代码看起来像这样:

 1 + 2; 

这段代码有什么作用? 那么,根据C / C ++编译器,它可能会编译成评估1 + 2的东西。 但那么结果存储在哪里? 你用它做什么吗? 没有? 那么为什么源代码的代码部分开始?

当然,你可以说代码实际上是a+b; ,目的是确保对a+b的评估不会抛出表示溢出的exception,但是这种情况非常罕见,以至于它的特殊情况只能掩盖真正的问题,而且它真的会只需将其分配给临时变量即可轻松修复。

在任何情况下,对于任何编程语言和/或运行时和/或环境中的任何function,如果某个function不可用,其原因不可用的原因有:

  • 设计不当
  • 没有正确指定
  • 它没有正确实施
  • 它没有经过适当的测试
  • 没有正确记录
  • 它没有优先于竞争function

所有这些都需要在应用程序Y的版本X中显示一个function,无论是C#4.0还是MS Works 7.0。

不,没有办法知道。

传入的所有内容都是对象引用。 无论是原位“新建”,还是源自数组,所讨论的方法都无法知道传入的参数是如何实例化的和/或在何处实现的。

知道在调用函数/方法之前是否已经创建了传递给函数(或方法)的对象的一种方法是该对象具有使用从系统函数传递的时间戳初始化的属性; 以这种方式,看着那个属性,就有可能解决问题。

坦率地说,我不会使用这种方法,因为

  • 如果传递的参数是一个正确创建的对象,或者它是在不同的时刻创建的,我现在看不出代码为什么会出现任何原因。
  • 我建议的方法取决于系统function,在某些系统中不能存在,或者可能不太可靠。
  • 使用比10年前使用的CPU更快的现代CPU,可能存在使用正确的阈值值来决定何时新创建对象的问题。

另一种解决方案是使用一个对象属性,该对象属性设置为来自对象创建者的值,并且该对象属性设置为与对象的所有方法不同的值。
在这种情况下,问题是忘记添加代码来更改每个方法中的属性。

我再次问自己“真的需要这样做吗?”。

作为一种可能的部分解决方案,如果你只想让一个对象被一个方法使用,那么你可以看一个Singleton。 这样,如果已存在,则该方法无法创建另一个实例。