我最近使用C#在Microsoft.Ink dll中工作并正在调试一个问题(与此无关)我注意到,当我调试它时,墨水对象有一个笔画对象,它有一个墨水对象,有....等。
这让我很困惑,因为我假设你不能这样做(我来自C ++背景)
但是我忽略了它,解决了问题,继续前进。今天,我遇到了一个类似的问题,因为我看到的是一个私有成员与自己属于同一类的类。
public sealed class Factory
{
private static Factory instance = new Factory();
}
怎么可能呢?我现在可以调用instance.instance.instance.instance ...等。正如你可以想象的那样,这伤害了我的大脑,我确信它在计算机上也不会好。编译器如何处理这个问题?兔子洞有多深?
答案 0 :(得分:9)
因为它是静态的,因此AppDomain中只有一个变量instance
。
你在考虑的是:
public class Foo
{
private Foo lol = new Foo();
}
注意,这里的一切都是实例,而不是静态的。
正如评论者所指出的那样(很久以前),这在语法上是有效的,但是会导致抛出StackOverflowException,因为赋值需要构造,构造会创建一个新的赋值。一个在一个循环中触发另一个循环,该循环在调用堆栈达到其最大长度时结束。
在OP的示例中,赋值需要构造,但赋值由static constructor触发,而不是实例构造函数。静态构造函数仅在AppDomain中执行一次,以初始化类'Type。它不是由实例构造触发的,因此(在OP示例中)不会导致堆栈溢出。
答案 1 :(得分:7)
它本质上不一定是递归的。想一个链表。或一棵树。
class Directory
{
string name;
Directory parentDirectory;
}
它只允许该类的对象具有对该类的另一个对象的内部引用。
答案 2 :(得分:6)
这是一种称为“Singleton”的软件模式。
有些人对问题的使用不满意,原因多于问题所述,但无论好坏,它都是.NET Framework中的常见模式。您将在要仅实例化一次的类上找到Singleton Properties(或字段)。可以将静态Instance属性视为挂起对象的全局钩子。
答案 3 :(得分:5)
由于这是一个类,而不是结构,当您声明一个类的字段时,您只是定义了对类的引用。这使您可以在分配参考时继续使用参考。
在你的情况下,你的引用会分配一个新类,但它是静态的,所以不管你创建了多少个类,它都只会执行一次。实例构造函数在第一次使用Factory时运行,并将调用单个非静态构造函数。不允许执行instance.instance.instance,因为实例是静态的。您无法从成员访问静态变量 - 您需要执行Factory.instance。
但是,你可以〜使实例非静态,并让它成为对其他“Factory”类的引用,甚至是对它的引用。在这种情况下,您可以链接instance.instance.instance - 但只要您设置它们,它就会跟随引用。一切正常,没有问题。
答案 4 :(得分:2)
只有一个'instance'实例,因为它是静态的。您应该能够访问它的唯一方法是调用Factory.instance。
string text = Factory.instance.ToString(); // legal
string text2 = Factory.instance.instance.ToString(); // compiler error
答案 5 :(得分:1)
我认为你应该反过来问:为什么不能这样做? Factory
只是一种类似于编译器解析的类型。
由于这里的大多数答案都指出这只是因为Factory
是一个静态字段,所以我添加了以下示例。请注意,这是一个非常原始的链式列表示例(由于各种原因,您可能不会以这种方式实现它,但我还没有提出更好的示例)。在此示例中,ChainedListItem
是单链接列表元素的容器,其中包含指向列表中下一个项目的相同类型的字段。该列表有一个(空)头元素,最后一个元素标记为空_nextItem
字段:
public class ChainedListItem<T>
{
private ChainedListItem<T> _nextItem;
T _content;
public ChainedListItem<T> NextItem
{
get { return _nextItem; }
set { _nextItem = value; }
}
public T Content
{
get { return _content; }
set { _content = value; }
}
public ChainedListItem<T> Add(T content)
{
_nextItem = new ChainedListItem<T>();
_nextItem.Content = content;
return _nextItem;
}
public void Dump()
{
ChainedListItem<T> current = this;
while ((current = current.NextItem) != null)
{
Console.WriteLine(current._content);
}
}
}
class Program
{
static void Main(string[] args)
{
ChainedListItem<int> chainedList = new ChainedListItem<int>();
chainedList.Add(1).Add(2).Add(3);
chainedList.Dump();
}
}
“兔子洞”与你的堆栈空间一样深,允许你再次调用该类型的构造函数。如果你试图深入这个,你会得到一个stackoverflow异常,就像任何其他递归一样。
顺便说一下,你在答案中编写的代码显示了Singleton的一个非常基本的实现,它实际上是基于一个与周围类型相同类型的(私有)静态成员。
而且,最后但并非最不重要的是,这样的结构在C ++中也非常好。
答案 6 :(得分:0)
这是singleton。这意味着该类只有一个实例。
这是全班吗?通常在C#中你会看到像
这样的单身public class SomeClass
{
static readonly SomeClass instance = new SomeClass();
public static SomeClass Instance
{
get { return instance; }
}
static SomeClass()
{
}
SomeClass()
{
}
}
答案 7 :(得分:0)
我不确定你是如何访问该实例的,因为它是私有的。唯一有用的是Singleton实现,但如果是这样的话,那么公共财产就会暴露实例。
答案 8 :(得分:-3)
这是大多数OO语言一直在做的。 instance是Factory的静态成员。这段代码没有什么不寻常之处。它是标准的工厂模式。您是否也遇到过这样的代码问题?
x = x + 1;