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

时间:2009-12-17 15:21:57

标签: c# .net parameters reference new-operator

短版

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

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

长版

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

var people = new List<Person>();

Person bob = new Person("Bob");

people.Add(bob);
people.Add(new Person("Larry"));

此处List<Person>.Add方法采用了“现有”Person(Bob)以及“新”Person(Larry),该列表包含两个项目。可以bobpeople[0]访问Bob。 Larry可以people[1]访问,如果需要,可以在此后以larry(或其他)缓存和访问。

好的,好的。但有时一个方法不应该传递一个新对象。举个例子,Array.Sort<T>。以下内容并没有多大意义:

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

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

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

DataTable firstTable = myDataSet.Tables["FirstTable"];
DataTable secondTable = myDataSet.Tables["SecondTable"];

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

正如我所说,这不是一个很好的例子,因为DataRowCollection.Add实际上并不期望 new 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
}

OR:

TryDoSomething(new StreamWriter(path));
嗯......似乎这会造成问题,不是吗?我构造了一个StreamWriter,它实现了IDisposable,但是TryDoSomething并不会假设知道它是否具有对output参数的独占访问权。因此,对象要么过早地处置(在第一种情况下),要么根本没有处置(在第二种情况下)。

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

9 个答案:

答案 0 :(得分:10)

不,基本上。

真的没有区别:

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

Foo(new ...);

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

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

答案 1 :(得分:6)

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

排序。

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

DoSomething(ref new Customer());

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

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

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

答案 2 :(得分:5)

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

答案 3 :(得分:3)

简短回答 - 没有没有

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

// 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
}

答案 4 :(得分:3)

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

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

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

    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。 但是,请不要在代码中使用它。

答案 5 :(得分:3)

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

  

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

简短的答案是:不。

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

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

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

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

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

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

就像代码看起来像这样:

1 + 2;

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

开头?

当然,您可以认为代码实际上是a+b;,目的是确保a+b的评估不会抛出表示溢出的异常,但是这样的情况是非常罕见的,它的特殊情况只会掩盖真正的问题,只需将它分配给一个临时变量,真的很简单。

在任何情况下,对于任何编程语言和/或运行时和/或环境中的任何功能,如果某项功能不可用,则无法获得该功能的原因包括:

  • 设计不合理
  • 未正确指定
  • 没有正确实施
  • 未经正确测试
  • 没有正确记录
  • 没有优先于竞争功能

要使所有这些功能都能在应用程序Y的X版本中出现,无论是C#4.0还是MS Works 7.0。

答案 6 :(得分:0)

不,没有办法知道。

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

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

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

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

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