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;