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.

132 lines
4.5 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. /// <value>the value wrapped by this <see cref="Ref{T}"/></value>
  33. public T Value
  34. {
  35. get
  36. {
  37. if (Error != null) throw Error;
  38. return _value;
  39. }
  40. set => _value = value;
  41. }
  42. private Exception _error;
  43. /// <summary>
  44. /// An exception that was generated while creating the value.
  45. /// </summary>
  46. /// <value>the error held in this <see cref="Ref{T}"/></value>
  47. public Exception Error
  48. {
  49. get
  50. {
  51. return _error;
  52. }
  53. set
  54. {
  55. value.SetStackTrace(new StackTrace(1));
  56. _error = value;
  57. }
  58. }
  59. /// <summary>
  60. /// Constructor.
  61. /// </summary>
  62. /// <param name="reference">the initial value of the reference</param>
  63. public Ref(T reference)
  64. {
  65. _value = reference;
  66. }
  67. /// <summary>
  68. /// Converts to referenced type, returning the stored reference.
  69. /// </summary>
  70. /// <param name="self">the object to be de-referenced</param>
  71. /// <returns>the value referenced by the object</returns>
  72. public static implicit operator T(Ref<T> self)
  73. {
  74. return self.Value;
  75. }
  76. /// <summary>
  77. /// Converts a value T to a reference to that object. Will overwrite the reference in the left hand expression if there is one.
  78. /// </summary>
  79. /// <param name="toConvert">the value to wrap in the Ref</param>
  80. /// <returns>the Ref wrapping the value</returns>
  81. public static implicit operator Ref<T>(T toConvert)
  82. {
  83. return new Ref<T>(toConvert);
  84. }
  85. /// <summary>
  86. /// Throws error if one was set.
  87. /// </summary>
  88. public void Verify()
  89. {
  90. if (Error != null) throw Error;
  91. }
  92. /// <summary>
  93. /// Compares the wrapped object to the other object.
  94. /// </summary>
  95. /// <param name="other">the object to compare to</param>
  96. /// <returns>the value of the comparison</returns>
  97. public int CompareTo(T other)
  98. {
  99. if (Value is IComparable<T> compare)
  100. return compare.CompareTo(other);
  101. return Equals(Value, other) ? 0 : -1;
  102. }
  103. /// <summary>
  104. /// Compares the wrapped object to the other wrapped object.
  105. /// </summary>
  106. /// <param name="other">the wrapped object to compare to</param>
  107. /// <returns>the value of the comparison</returns>
  108. public int CompareTo(Ref<T> other) => CompareTo(other.Value);
  109. }
  110. internal static class ExceptionUtilities
  111. {
  112. private static readonly FieldInfo StackTraceStringFi = typeof(Exception).GetField("_stackTraceString", BindingFlags.NonPublic | BindingFlags.Instance);
  113. private static readonly Type TraceFormatTi = Type.GetType("System.Diagnostics.StackTrace")?.GetNestedType("TraceFormat", BindingFlags.NonPublic);
  114. private static readonly MethodInfo TraceToStringMi = typeof(StackTrace).GetMethod("ToString", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { TraceFormatTi }, null);
  115. public static Exception SetStackTrace(this Exception target, StackTrace stack)
  116. {
  117. var getStackTraceString = TraceToStringMi.Invoke(stack, new[] { Enum.GetValues(TraceFormatTi).GetValue(0) });
  118. StackTraceStringFi.SetValue(target, getStackTraceString);
  119. return target;
  120. }
  121. }
  122. }