I was working in the Microsoft.Ink dll recently using C# and was debugging a problem (which is not related to this) I noticed, that when I was debugging it, ink objects had a strokes object, which had an ink object, which had.... etc.
我最近在使用C#工作在Microsoft.Ink dll并正在调试一个问题(与此无关)我注意到,当我调试它时,墨水对象有一个笔画对象,它有一个墨水对象, ....等
This confused me, as I was under the assumption you could not do this (I come from a C++ Background)
这让我很困惑,因为我假设你不能这样做(我来自C ++背景)
But I ignored it, solved the problem, and moved on. Today, I run into a similar problem, as I look at a class which had a private member which was the same class as itself.
但是我忽略了它,解决了问题,继续前进。今天,我遇到了类似的问题,因为我看到一个有私有成员的类,它与自己是同一个类。
public sealed class Factory
{
private static Factory instance = new Factory();
}
How is that even possible? I can now call instance.instance.instance.instance...etc. This, as you can imagine, hurts my mortal brain, and I'm sure it can't be good on the computer either. How does the compiler deal with this? And Just how deep does the rabbit hole go?
这怎么可能呢?我现在可以调用instance.instance.instance.instance ...等。正如你可以想象的那样,这伤害了我的大脑,我确信它在计算机上也不会好。编译器如何处理这个问题?兔子洞有多深?
9 个解决方案
#1
Because it's static and therefore there is only one copy of the variable instance
within the AppDomain.
因为它是静态的,因此AppDomain中只有一个变量实例副本。
What you're thinking of is this:
你在想什么是这样的:
public class Foo
{
private Foo lol = new Foo();
}
Notice, everything here is instance, not static.
注意,这里的一切都是实例,而不是静态的。
As the commenters noted (long ago), this is valid syntactically, but would result in a *Exception being thrown, as the assignment requires construction, and construction creates a new assignment. One triggers the other in a cycle that ends when the call stack reaches its maximum length.
正如评论者所指出的那样(很久以前),这在语法上是有效的,但是会导致抛出*Exception,因为赋值需要构造,而构造会创建一个新的赋值。一个在一个循环中触发另一个循环,该循环在调用堆栈达到其最大长度时结束。
In OP's example, assignment requires construction, but the assignment is triggered by the static constructor, not the instance constructor. The static constructor only executes once within an AppDomain, in order to initialize the class' Type. It isn't triggered by instance construction, and so (in OP's example) won't result in a stack overflow.
在OP的示例中,赋值需要构造,但赋值由静态构造函数触发,而不是实例构造函数。静态构造函数仅在AppDomain中执行一次,以初始化类'Type。它不是由实例构造触发的,因此(在OP的示例中)不会导致堆栈溢出。
#2
This is a software pattern known as "Singleton".
这是一种称为“Singleton”的软件模式。
Some people frown upon the use of the pattern for more reasons than just stated in the question but for better or for worse it is a common pattern in the .NET Framework. You will find Singleton Properties (or fields) on classes that are meant to be instantiated only once. Think of a static Instance property as a global hook upon which to hang an object.
有些人不满意使用该模式的原因比问题中所述的更多,但无论好坏,它都是.NET Framework中的常见模式。您将在要实例化一次的类上找到Singleton Properties(或字段)。将静态Instance属性视为挂起对象的全局钩子。
#3
it's not necessarily recursive by nature. think of a linked list. or a tree.
它本质上不一定是递归的。想一个链表。或一棵树。
class Directory
{
string name;
Directory parentDirectory;
}
It's just allows objects of that class to have an internal reference to another object of that class.
它只允许该类的对象具有对该类的另一个对象的内部引用。
#4
Since this is a class, and not a struct, when you declare a field that is the class, you are only defining a reference to a class. This allows you to keep having references, provided you assign them.
由于这是一个类,而不是结构,因此当您声明一个类的字段时,您只是定义了对类的引用。这允许您在分配参考时继续使用引用。
In your case, you're reference allocates a new class, but it is static, so it's only going to do it one time, no matter how many classes you create. The instance constructor runs the first time Factory is used, and will call a single non-static constructor. Doing instance.instance.instance is not allowed, since instance is static. You cannot access a static variable from a member - you need to do Factory.instance.
在你的情况下,你的引用会分配一个新类,但它是静态的,所以无论你创建了多少个类,它都只会执行一次。实例构造函数在第一次使用Factory时运行,并将调用单个非静态构造函数。不允许执行instance.instance.instance,因为实例是静态的。您无法从成员访问静态变量 - 您需要执行Factory.instance。
However, you ~could~ make instance non-static, and have it be a reference to some other "Factory" class, or even a reference to this. In that case, you could chain instance.instance.instance - but it will just follow the references as long as you've set them. Everything works, no problems.
但是,你可以〜使实例非静态,并使它成为对其他“Factory”类的引用,甚至是对它的引用。在这种情况下,您可以链接instance.instance.instance - 但只要您设置它们,它就会跟随引用。一切正常,没有问题。
#5
There will only ever be one instance of 'instance' because it is static. The only way you should be able to access it is by calling Factory.instance.
只有一个'instance'实例,因为它是静态的。您应该能够访问它的唯一方法是调用Factory.instance。
string text = Factory.instance.ToString(); // legal
string text2 = Factory.instance.instance.ToString(); // compiler error
#6
I think you should ask the other way around: Why shouldn't this be possible? Factory
is just a type like any type which gets resolved by the compiler.
我想你应该反过来问:为什么不能这样做? Factory只是一种类似于编译器解析的类型。
As most of the answers here point out that this is working only because Factory
is a static field, I have added the following sample. Please note that this is a very primitive sample of a chained list (you probably wouldn't implement it that way for various reasons, but I didn't come up with a better example yet). In this example, ChainedListItem
is a container for an element of a single-linked list, which contains a field of the very same type to point to the next item in the list. The list has an (empty) head element and the last element is marked by having an empty _nextItem
field:
由于这里的大多数答案都指出这只是因为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();
}
}
The "rabbit hole" goes as deep as your stack space allows you to make another call to the constructor of the type. If you try to go deeper than that, you will get a * exception as with any other recursion.
“兔子洞”与你的堆栈空间一样深,允许你再次调用该类型的构造函数。如果您尝试深入此过程,您将获得与任何其他递归一样的*异常。
By the way, the code that you wrote in your answer is showing a very basic implementation of a Singleton which is actually based on having a (private) static member of the same type as the surrounding type.
顺便说一下,你在答案中编写的代码显示了一个非常基本的Singleton实现,它实际上是基于一个与周围类型相同类型的(私有)静态成员。
And, last but not least, such constructs are also perfectly fine in C++.
而且,最后但并非最不重要的是,这样的结构在C ++中也完美无缺。
#7
It is a singleton. Meaning there is really only one instance of the class.
这是一个单身人士。意思是实际上只有一个类的实例。
Is that the entire class? Typically in C# you will see a singleton like
这是全班吗?通常在C#中你会看到像单身一样的单身人士
public class SomeClass
{
static readonly SomeClass instance = new SomeClass();
public static SomeClass Instance
{
get { return instance; }
}
static SomeClass()
{
}
SomeClass()
{
}
}
#8
I'm not sure how you would even access the instance since it is private. The only thing this would be useful for is a Singleton implementation, but if that is the case you are mission the public property exposing the instance.
我不确定你是如何访问实例的,因为它是私有的。唯一有用的是Singleton实现,但如果是这种情况,那么你就是公开属性暴露实例的任务。
#9
This is done all the time is most OO languages. instance is a static member of Factory. There is nothing unusual about this code. It is standard Factory pattern. Do you also have a problem with code like this?
这是大多数OO语言一直在做的。 instance是Factory的静态成员。这段代码没有什么不寻常之处。它是标准的工厂模式。您是否也遇到过这样的代码问题?
x = x + 1;
#1
Because it's static and therefore there is only one copy of the variable instance
within the AppDomain.
因为它是静态的,因此AppDomain中只有一个变量实例副本。
What you're thinking of is this:
你在想什么是这样的:
public class Foo
{
private Foo lol = new Foo();
}
Notice, everything here is instance, not static.
注意,这里的一切都是实例,而不是静态的。
As the commenters noted (long ago), this is valid syntactically, but would result in a *Exception being thrown, as the assignment requires construction, and construction creates a new assignment. One triggers the other in a cycle that ends when the call stack reaches its maximum length.
正如评论者所指出的那样(很久以前),这在语法上是有效的,但是会导致抛出*Exception,因为赋值需要构造,而构造会创建一个新的赋值。一个在一个循环中触发另一个循环,该循环在调用堆栈达到其最大长度时结束。
In OP's example, assignment requires construction, but the assignment is triggered by the static constructor, not the instance constructor. The static constructor only executes once within an AppDomain, in order to initialize the class' Type. It isn't triggered by instance construction, and so (in OP's example) won't result in a stack overflow.
在OP的示例中,赋值需要构造,但赋值由静态构造函数触发,而不是实例构造函数。静态构造函数仅在AppDomain中执行一次,以初始化类'Type。它不是由实例构造触发的,因此(在OP的示例中)不会导致堆栈溢出。
#2
This is a software pattern known as "Singleton".
这是一种称为“Singleton”的软件模式。
Some people frown upon the use of the pattern for more reasons than just stated in the question but for better or for worse it is a common pattern in the .NET Framework. You will find Singleton Properties (or fields) on classes that are meant to be instantiated only once. Think of a static Instance property as a global hook upon which to hang an object.
有些人不满意使用该模式的原因比问题中所述的更多,但无论好坏,它都是.NET Framework中的常见模式。您将在要实例化一次的类上找到Singleton Properties(或字段)。将静态Instance属性视为挂起对象的全局钩子。
#3
it's not necessarily recursive by nature. think of a linked list. or a tree.
它本质上不一定是递归的。想一个链表。或一棵树。
class Directory
{
string name;
Directory parentDirectory;
}
It's just allows objects of that class to have an internal reference to another object of that class.
它只允许该类的对象具有对该类的另一个对象的内部引用。
#4
Since this is a class, and not a struct, when you declare a field that is the class, you are only defining a reference to a class. This allows you to keep having references, provided you assign them.
由于这是一个类,而不是结构,因此当您声明一个类的字段时,您只是定义了对类的引用。这允许您在分配参考时继续使用引用。
In your case, you're reference allocates a new class, but it is static, so it's only going to do it one time, no matter how many classes you create. The instance constructor runs the first time Factory is used, and will call a single non-static constructor. Doing instance.instance.instance is not allowed, since instance is static. You cannot access a static variable from a member - you need to do Factory.instance.
在你的情况下,你的引用会分配一个新类,但它是静态的,所以无论你创建了多少个类,它都只会执行一次。实例构造函数在第一次使用Factory时运行,并将调用单个非静态构造函数。不允许执行instance.instance.instance,因为实例是静态的。您无法从成员访问静态变量 - 您需要执行Factory.instance。
However, you ~could~ make instance non-static, and have it be a reference to some other "Factory" class, or even a reference to this. In that case, you could chain instance.instance.instance - but it will just follow the references as long as you've set them. Everything works, no problems.
但是,你可以〜使实例非静态,并使它成为对其他“Factory”类的引用,甚至是对它的引用。在这种情况下,您可以链接instance.instance.instance - 但只要您设置它们,它就会跟随引用。一切正常,没有问题。
#5
There will only ever be one instance of 'instance' because it is static. The only way you should be able to access it is by calling Factory.instance.
只有一个'instance'实例,因为它是静态的。您应该能够访问它的唯一方法是调用Factory.instance。
string text = Factory.instance.ToString(); // legal
string text2 = Factory.instance.instance.ToString(); // compiler error
#6
I think you should ask the other way around: Why shouldn't this be possible? Factory
is just a type like any type which gets resolved by the compiler.
我想你应该反过来问:为什么不能这样做? Factory只是一种类似于编译器解析的类型。
As most of the answers here point out that this is working only because Factory
is a static field, I have added the following sample. Please note that this is a very primitive sample of a chained list (you probably wouldn't implement it that way for various reasons, but I didn't come up with a better example yet). In this example, ChainedListItem
is a container for an element of a single-linked list, which contains a field of the very same type to point to the next item in the list. The list has an (empty) head element and the last element is marked by having an empty _nextItem
field:
由于这里的大多数答案都指出这只是因为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();
}
}
The "rabbit hole" goes as deep as your stack space allows you to make another call to the constructor of the type. If you try to go deeper than that, you will get a * exception as with any other recursion.
“兔子洞”与你的堆栈空间一样深,允许你再次调用该类型的构造函数。如果您尝试深入此过程,您将获得与任何其他递归一样的*异常。
By the way, the code that you wrote in your answer is showing a very basic implementation of a Singleton which is actually based on having a (private) static member of the same type as the surrounding type.
顺便说一下,你在答案中编写的代码显示了一个非常基本的Singleton实现,它实际上是基于一个与周围类型相同类型的(私有)静态成员。
And, last but not least, such constructs are also perfectly fine in C++.
而且,最后但并非最不重要的是,这样的结构在C ++中也完美无缺。
#7
It is a singleton. Meaning there is really only one instance of the class.
这是一个单身人士。意思是实际上只有一个类的实例。
Is that the entire class? Typically in C# you will see a singleton like
这是全班吗?通常在C#中你会看到像单身一样的单身人士
public class SomeClass
{
static readonly SomeClass instance = new SomeClass();
public static SomeClass Instance
{
get { return instance; }
}
static SomeClass()
{
}
SomeClass()
{
}
}
#8
I'm not sure how you would even access the instance since it is private. The only thing this would be useful for is a Singleton implementation, but if that is the case you are mission the public property exposing the instance.
我不确定你是如何访问实例的,因为它是私有的。唯一有用的是Singleton实现,但如果是这种情况,那么你就是公开属性暴露实例的任务。
#9
This is done all the time is most OO languages. instance is a static member of Factory. There is nothing unusual about this code. It is standard Factory pattern. Do you also have a problem with code like this?
这是大多数OO语言一直在做的。 instance是Factory的静态成员。这段代码没有什么不寻常之处。它是标准的工厂模式。您是否也遇到过这样的代码问题?
x = x + 1;