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.

108 lines
3.5 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> : IComparable<T>, IComparable<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. /// <inheritdoc />
  75. public int CompareTo(T other)
  76. {
  77. if (Value is IComparable<T> compare)
  78. return compare.CompareTo(other);
  79. return Equals(Value, other) ? 0 : -1;
  80. }
  81. /// <inheritdoc />
  82. public int CompareTo(Ref<T> other)
  83. {
  84. return CompareTo(other.Value);
  85. }
  86. }
  87. internal static class ExceptionUtilities
  88. {
  89. private static readonly FieldInfo StackTraceStringFi = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
  90. private static readonly Type TraceFormatTi = Type.GetType("System.Diagnostics.StackTrace")?.GetNestedType("TraceFormat", BindingFlags.NonPublic);
  91. private static readonly MethodInfo TraceToStringMi = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TraceFormatTi }, null);
  92. public static Exception SetStackTrace(this Exception target, StackTrace stack)
  93. {
  94. var getStackTraceString = TraceToStringMi.Invoke(stack, new[] { Enum.GetValues(TraceFormatTi).GetValue(0) });
  95. StackTraceStringFi.SetValue(target, getStackTraceString);
  96. return target;
  97. }
  98. }
  99. }