Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.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#_.net_Parameters_Reference_New Operator - Fatal编程技术网

C# 我是否可以检测到我';您是否已被指定一个新对象作为参数? 短版

C# 我是否可以检测到我';您是否已被指定一个新对象作为参数? 短版,c#,.net,parameters,reference,new-operator,C#,.net,Parameters,Reference,New Operator,对于那些没有时间阅读我对以下问题的推理的人: 有没有办法对方法的参数强制执行“仅限新对象”或“仅限现有对象”的策略 长版本 有很多方法将对象作为参数,而不管方法是否将对象“全部归自身”。例如: var people = new List<Person>(); Person bob = new Person("Bob"); people.Add(bob); people.Add(new Person("Larry")); 上面的代码所做的就

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

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

长版本 有很多方法将对象作为参数,而不管方法是否将对象“全部归自身”。例如:

var people = new List<Person>();

Person bob = new Person("Bob");

people.Add(bob);
people.Add(new Person("Larry"));
上面的代码所做的就是获取一个新数组,对它进行排序,然后忘记它(因为在
array.sort
退出后,它的引用计数达到零,因此,如果我没有弄错的话,排序后的数组将被垃圾收集)。因此,
Array.Sort
需要一个“现有”数组作为其参数

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

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

firstTable.Rows.Add(secondTable.Rows[0]);
正如我所说,这不是一个很好的例子,因为
DataRowCollection.Add
实际上并不期望新的
DataRow
;但它确实需要一个不属于
DataTable
DataRow
。所以上面代码的最后一行行不通;它需要:

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
}
或:

嗯……这似乎是一个问题,不是吗?我构造了一个
StreamWriter
,它实现了
IDisposable
,但是
TryDoSomething
不会假定它是否拥有对其
输出
参数的独占访问权限。因此,对象要么被过早地处置(在第一种情况下),或者根本没有得到处置(在第二种情况下)

我并不是说这一定是一个伟大的设计。也许乔什·斯托多拉是对的,这从一开始就是一个坏主意。不管怎样,我问这个问题主要是因为我好奇这样的事情是否可能。看起来答案是:不是真的。

基本上不是

这两者之间真的没有区别:

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

事实上,有时您可能会出于调试目的在两者之间进行转换


请注意,在
DataRow
/
DataTable
示例中,有一种替代方法,即DataRow可以知道其父行作为其状态的一部分。这与“新建”或“不新建”不同,可以使用“分离”例如,操作。根据对象的真实硬状态和快速状态定义条件比“新建”之类的模糊术语更有意义。如果有某种原因需要这样做,则代码的体系结构不正确。

简短回答-不,没有

在绝大多数情况下,我通常会发现上面列出的问题其实并不重要。当它们发生时,你可以重载一个方法,这样你就可以接受其他东西作为参数,而不是你担心共享的对象

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

不,没有办法知道


所有传入的都是对象引用。无论它是就地“新建”的,还是来自数组,所讨论的方法都无法知道传入的参数是如何实例化的和/或在哪里实例化的。

是的,有一种方法可以做到这一点。

有点

如果将参数设置为
ref
参数,则必须使用现有变量作为参数。不能执行以下操作:

DoSomething(ref new Customer());
1 + 2;
如果这样做,您将得到错误“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
    }
    
    DoSomething(ref new Customer());
    
        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;
            }
        }
    
    if (ReferenceCount(obj) == 1) return; // only reference is the one we have
    
    1 + 2;