You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
3.1 KiB

  1. using System;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. namespace IPA.Utilities
  5. {
  6. /// <summary>
  7. /// A class to store a reference for passing to methods which cannot take ref parameters.
  8. /// </summary>
  9. /// <typeparam name="T">the type of the value</typeparam>
  10. public class Ref<T>
  11. {
  12. private T _value;
  13. /// <summary>
  14. /// The value of the reference
  15. /// </summary>
  16. public T Value
  17. {
  18. get
  19. {
  20. if (Error != null) throw Error;
  21. return _value;
  22. }
  23. set => _value = value;
  24. }
  25. private Exception _error;
  26. /// <summary>
  27. /// An exception that was generated while creating the value.
  28. /// </summary>
  29. public Exception Error
  30. {
  31. get
  32. {
  33. return _error;
  34. }
  35. set
  36. {
  37. value.SetStackTrace(new StackTrace(1));
  38. _error = value;
  39. }
  40. }
  41. /// <summary>
  42. /// Constructor.
  43. /// </summary>
  44. /// <param name="reference">the initial value of the reference</param>
  45. public Ref(T reference)
  46. {
  47. _value = reference;
  48. }
  49. /// <summary>
  50. /// Converts to referenced type, returning the stored reference.
  51. /// </summary>
  52. /// <param name="self">the object to be de-referenced</param>
  53. /// <returns>the value referenced by the object</returns>
  54. public static implicit operator T(Ref<T> self)
  55. {
  56. return self.Value;
  57. }
  58. /// <summary>
  59. /// Converts a value T to a reference to that object. Will overwrite the reference in the left hand expression if there is one.
  60. /// </summary>
  61. /// <param name="toConvert">the value to wrap in the Ref</param>
  62. /// <returns>the Ref wrapping the value</returns>
  63. public static implicit operator Ref<T>(T toConvert)
  64. {
  65. return new Ref<T>(toConvert);
  66. }
  67. /// <summary>
  68. /// Throws error if one was set.
  69. /// </summary>
  70. public void Verify()
  71. {
  72. if (Error != null) throw Error;
  73. }
  74. }
  75. internal static class ExceptionUtilities
  76. {
  77. private static readonly FieldInfo StackTraceStringFi = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
  78. private static readonly Type TraceFormatTi = Type.GetType("System.Diagnostics.StackTrace")?.GetNestedType("TraceFormat", BindingFlags.NonPublic);
  79. private static readonly MethodInfo TraceToStringMi = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TraceFormatTi }, null);
  80. public static Exception SetStackTrace(this Exception target, StackTrace stack)
  81. {
  82. var getStackTraceString = TraceToStringMi.Invoke(stack, new[] { Enum.GetValues(TraceFormatTi).GetValue(0) });
  83. StackTraceStringFi.SetValue(target, getStackTraceString);
  84. return target;
  85. }
  86. }
  87. }