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.

125 lines
4.0 KiB

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