Scott DePouw
Scott DePouw

Reputation: 3889

Set C# Property that has no Setter

In C#, there is the Exception class. For the purposes of testing, I would like to be able to set its StackTrace property to arbitrary strings. StackTrace has no setter, so my first attempt was to try using reflection:

Exception instance = new Exception("Testing");
PropertyInfo propertyInfo = typeof(Exception).GetProperty("StackTrace");
propertyInfo.SetValue(instance, "Sample StackTrace value", null);

This yields the runtime error:

System.ArgumentException : Property set method not found.

Is there any way I could set the StackTrace property? More generally, is there a way to set a property that lacks a setter?

Upvotes: 3

Views: 6879

Answers (4)

vtortola
vtortola

Reputation: 35885

That property is implemented this way:

public virtual string StackTrace
{
    [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    get
    {
        return this.GetStackTrace(true);
    }
}

And that called method is implemented this way:

private string GetStackTrace(bool needFileInfo)
{
    string text = this._stackTraceString;
    string text2 = this._remoteStackTraceString;
    if (!needFileInfo)
    {
        text = this.StripFileInfo(text, false);
        text2 = this.StripFileInfo(text2, true);
    }
    if (text != null)
    {
        return text2 + text;
    }
    if (this._stackTrace == null)
    {
        return text2;
    }
    string stackTrace = Environment.GetStackTrace(this, needFileInfo);
    return text2 + stackTrace;
}

So it seems that if you set the field _stackTraceString to any String, you will get _stackTraceString + _remoteStackTraceString.

You can set fields with FieldInfo.SetValue.

I got this information using http://ilspy.net.

Do not do this on production. Things are designed in a specific way for a reason, just the exposed API is guaranteed, a minor upgrade could change this internal implementation detail and break your code, so never rely on internal details, just exposed APIs.

Cheers.

Upvotes: 2

Alessandro D'Andria
Alessandro D'Andria

Reputation: 8868

You can modify the backing field of the object:

Exception instance = new Exception("Testing");

var f = typeof(Exception).GetField("_stackTraceString", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField);

f.SetValue(instance, "hello");

This should work, to get a list of private fields you can use:

var fields = typeof(Exception).GetFields(BindingFlags.Instance | BindingFlags.NonPublic);

Personally I prefer the solution proposed by Matthew Watson.

Upvotes: 2

Matthew Watson
Matthew Watson

Reputation: 109537

You could derive your own exception class and override the StackTrace property, which is virtual:

public sealed class MyException: Exception
{
    public MyException(string message, string stackTrace): base(message)
    {
        _stackTrace = stackTrace;
    }

    public override string StackTrace
    {
        get
        {
            return _stackTrace;
        }
    }

    private readonly string _stackTrace;
}

Note that to do this properly, you should really implement all the standard exception constructors, but for unit testing this might not be strictly necessary.

See Designing Custom Exceptions for more details:

Upvotes: 10

Sargis Koshkaryan
Sargis Koshkaryan

Reputation: 1030

The StackTrace property of an Exception object is set by the runtime during stack walk, so we cannot set it manually.

Upvotes: 0

Related Questions