简体   繁体   中英

Structs and stackoverflow C#

Hi I have this program that I am writing that uses the struct below. I keep getting a stackoverflow error and the exception is stops the program at the fist bracket in public string sessionID set...(where the ">>>" is).

  public struct SessionStruct 
{
    public string sessionID
    {
        get
        {
            return sessionID;
        }
        set
  >>>   {
            sessionID = value;
        }
    } 
    public DateTime time
    {
        get
        {
            return time;
        }
        set
        {
            time = value;
        }
    }

    public string type
    {
        get
        {
            return type;
        }
        set
        {
            type = value;
        }
    } 
};

Here is the code that sets the struct:

if (type == "11" || type == "9")
                {
                    s.sessionID = attributeArray[0].ToString();
                    s.time = DateTime.Now;
                    if (type == "9")
                        s.type = attributeArray[4].ToString();
                }
                else
                {
                    s.sessionID = null;
                    s.time = DateTime.Now;
                    s.type = null;
                }

Thanks for the help in advance...

you're doing an infinite recursion in that setter. Think about it. Create a private member of a different name to get/set.

private string sessionID;
public string SessionID
    {
        get
        {
            return sessionID;
        }
        set
        {
            sessionID = value;
        }
    }

sessionId属性设置器将其称为self,从而导致永恒递归。

Your property is calling itself over and over again. The property really needs a backing store to store the data in, which is typically a private variable:

private string _sessionID;

public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }
} 

Or let the compiler do it for you:

public string sessionID
{
    // compiler will create a backing store for you
    get; set; 
} 

That is because the set property calls itself (the name is the same).

You probably want to use an autoproperty

public string sessionID
{
    get; set;
} 

The problem is that you're recursing on the sessionID property.

This:

public string sessionID
{
    get
    {
        return sessionID;
    }
    set
    {
        sessionID = value;
    }
}

Will be compiled to something like this:

public string sessionID_get()           { return sessionID_get(); }
public void sessionID_set(string value) { sessionID_set(value);   }

Obviously this won't work!

You should be using a backing field:

private string _sessionID;
public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }
}

Alternatively, you can get the compiler to automatically generate one for you:

public string sessionID
{
    get; set;
}

Like everyone else has already said, you are doing an infinite recursion. Unlike everyone else, I'm going to explain you why an infinite recursion causes a stack overflow:

First, you need to understand that properties are just getter and setter methods (or functions, to speak more generically), only that they have a special syntax aimed to make the code that uses them more readable.

When some code attempts to set the property, behind the scenes the setter function is called, passing the value as an argument. Internally, your s.sessionID = attributeArray[0].ToString(); is essentially s.set_sessionID(attributeArray[0].ToString()); . So your setter is being called. But the setter also attempts to set the property. In fact, the sessionID = value; is just a cleaner way to say set_sessionID(value); . So your setter is invoking the setter, again. This invocation will attempt to run the sessionID = value; statement, which invokes the setter, which attempts to run the statement, which invokes the setter, which... ok, I hope you get the idea.

A function that invokes itself is said to be recursive , and the technique itself is normally refered to as recursion . There are some good uses for recursion, but normally they use some form of conditional branching (so, eventually, the condition fails and the function stops calling itself and starts yielding results). On your case, however, you are going to the infinite and beyond: there is nothing on your code to stop the recursion (mostly because it wasn't intended to be recursive after all), so it will go on indefinitely. Well, not indefinitely, just until it crashes your program :P

Now, into the juicy part of this answer: what the heck is a StackOverflow ? (aside from a nice Q/A website). Why aren't you getting something like an InfiniteRecursionException ? Of course, the runtime doesn't know whether you messed up or you are just doing something unusual (actually, the runtime hopes you are doing something sane), so it trusts you and keeps doing what your code is telling it to do (in this case, call the property setter).

But for a function call to work, the runtime needs to push a "pointer" to the calling statemenet and the call arguments into the stack. The pointer is needed so the runtime can figure out where to go once the called function returns. And the arguments are needed so the function will know where to look for them (after all, it doesn't know from where will it be called). On each call, these things are pushed to the top of the stack, on top of everything that was already there.

Normally, the function would pop (retrieve and remove) the arguments, do some stuff, and return. At that point (the arguments aren't there anymore), the "return pointer" is on the top of the stack, so the runtime pops it and uses it to give control back to the caller.

It is because of this stacking mechanism (everything pushed goes on top of everything else, and stuff is only popped from the top of the stack) that chained calls can work (for example: function A calls function B which calls function C: when C returns, control will go back to B, not directly to A; but once B itself returns, control does go back to A).

What happens if you keep pouring water onto a glass? Normally it will fill up (unless it has a hole or something). But what if you keep pouring water onto it when it's already full? The water flows out of the glass, because it can't fit in. We could say that the water overflows from the glass.

Now, let's look again to your infinite recursion: each time you're setting the property, you are making (implicitly) a call to the setter, so a new "return pointer" and an argument (the value) are pushed onto the stack. Since the call makes a new call before it has a chance to return, it is pushing return pointers to the stack indefinitely before they get a chance to be popped. So the stack fills up, until it has no more memory available. At that point, the stack overflows. And, because there is absolutely no way for the program to continue without pushing stuff on the stack (which the runtime can't do because there is no more room for that stuff), the only option left is to complain about the issue. This complain from the runtime is what is commonly known as a StackOverflowException .


BTW, you can see (a simplified sample) of the call stack while debugging your program. Run it through Visual Studio's debugger (by default, you can do that by pressing F5) and wait for it to crash with the SO exception. Then look for the "Call Stack" window (I can't remember where it appears by default, have moved it countless times), and you will see how deep the runtime has been able to go on the recursion before running out of stack space. Your setter should show up there line after line, countless times.


And finally, a mention on the solutions: all you need to do is to avoid recursion. This means that you can't call your setter from inside your setter (by setting the same property from the setter). This also applies to getters (which are called upon getting).

The typical way to do this is to define a "backing" field for the property (a member variable where the value will actually be stored), and then from your setter and getter you set or return that field, respectively. Since the field needs to have a different name (a typical approach is to use an underscore ( _ ) followed by the name of the property), the compiler will know that you are setting the field and not the property, so won't try to recursively call your property's setter. Normally, setters are useful to perform "Sanity checks" (for example, you could check that the passed value is not null ), but there are many situations when there is no need for checks on neither the setter nor the getter, and having to define the backing fields and the setters/getters themselves becomes boring, bloated, redundant, and error-prone. For this reason, C# supports "auto-implemented" properties: if you replace both accessors with get; set; get; set; (and the property is not abstract), the compiler will add a "nameless" backing field and the getting/setting code for you. Note that for this to work, you have to leave both accessors: if you want to define one of them, you'll also need to define the other.

If you have already understood the concepts, loot at the other answers to see many examples on how to apply them ;)

Hope this helps.

You have to use a backing field if you implement getter and setter yourself, otherwise it will keep calling itself on the setter, ie like this:

private string _sessionId;
public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }

}

Since you don't actually need the implementations as you didn't put in any custom logic, use automatic properties:

public string sessionID {get;set;}

You're assigning sessionId to itself in your set accessor. (Recursively)

You need to split the field you're assinging to it's own backing field, or use an automatic property.

Like so:

  private string _sessionId;

    public string sessionID
    {
        get
        {
            return _sessionId;
        }
        set
        {
            _sessionId= value;
        }
    } 

or automatic property:

public string sessionId { get;set ;}

Also note : You're other properties are going to have the same issue when you access the set accessor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM