A game framework written with osu! in mind.
fork

Configure Feed

Select the types of activity you want to include in your feed.

Reduce allocations in IScreen extension methods

+64 -36
+37
osu.Framework.Benchmarks/BenchmarkScreenExtensions.cs
··· 1 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2 + // See the LICENCE file in the repository root for full licence text. 3 + 4 + using BenchmarkDotNet.Attributes; 5 + using NUnit.Framework; 6 + using osu.Framework.Screens; 7 + 8 + namespace osu.Framework.Benchmarks 9 + { 10 + public class BenchmarkScreenExtensions : GameBenchmark 11 + { 12 + private Screen testScreen; 13 + 14 + [Test] 15 + [Benchmark] 16 + public void IsCurrentScreen() => testScreen.IsCurrentScreen(); 17 + 18 + protected override Game CreateGame() => new TestGame(testScreen = new Screen()); 19 + 20 + private class TestGame : Game 21 + { 22 + private readonly Screen screen; 23 + 24 + public TestGame(Screen screen) 25 + { 26 + this.screen = screen; 27 + } 28 + 29 + protected override void LoadComplete() 30 + { 31 + base.LoadComplete(); 32 + 33 + Add(new ScreenStack(screen)); 34 + } 35 + } 36 + } 37 + }
+26 -35
osu.Framework/Screens/IScreen.cs
··· 52 52 /// <param name="screen">The <see cref="IScreen"/> to push to.</param> 53 53 /// <param name="newScreen">The <see cref="IScreen"/> to push.</param> 54 54 public static void Push(this IScreen screen, IScreen newScreen) 55 - => runOnRoot(screen, stack => stack.Push(screen, newScreen), () => throw new InvalidOperationException($"Cannot {nameof(Push)} to a non-loaded {nameof(IScreen)} directly. Consider using {nameof(ScreenStack.Push)} instead.")); 55 + { 56 + var stack = getStack(screen); 57 + 58 + if (stack == null) 59 + throw new InvalidOperationException($"Cannot {nameof(Push)} to a non-loaded {nameof(IScreen)} directly. Consider using {nameof(ScreenStack.Push)} instead."); 60 + 61 + stack.Push(screen, newScreen); 62 + } 56 63 57 64 /// <summary> 58 65 /// Exits from an <see cref="IScreen"/>. 59 66 /// </summary> 60 67 /// <param name="screen">The <see cref="IScreen"/> to exit from.</param> 61 68 public static void Exit(this IScreen screen) 62 - => runOnRoot(screen, stack => stack.Exit(screen), () => screen.ValidForPush = false); 69 + { 70 + var stack = getStack(screen); 71 + 72 + if (stack == null) 73 + screen.ValidForPush = false; 74 + else 75 + stack.Exit(screen); 76 + } 63 77 64 78 /// <summary> 65 79 /// Makes an <see cref="IScreen"/> the current screen, exiting all child <see cref="IScreen"/>s along the way. 66 80 /// </summary> 67 81 /// <param name="screen">The <see cref="IScreen"/> to make current.</param> 68 82 public static void MakeCurrent(this IScreen screen) 69 - => runOnRoot(screen, stack => stack.MakeCurrent(screen)); 83 + => getStack(screen)?.MakeCurrent(screen); 70 84 71 85 /// <summary> 72 86 /// Retrieves whether an <see cref="IScreen"/> is the current screen. ··· 75 89 /// <param name="screen">The <see cref="IScreen"/> to check.</param> 76 90 /// <returns>True if <paramref name="screen"/> is the current screen.</returns> 77 91 public static bool IsCurrentScreen(this IScreen screen) 78 - => runOnRoot(screen, stack => stack.IsCurrentScreen(screen), () => false); 92 + => getStack(screen)?.IsCurrentScreen(screen) ?? false; 79 93 80 94 /// <summary> 81 95 /// Retrieves the child <see cref="IScreen"/> of an <see cref="IScreen"/>. ··· 83 97 /// <param name="screen">The <see cref="IScreen"/> to retrieve the child of.</param> 84 98 /// <returns>The child <see cref="IScreen"/> of <paramref name="screen"/>, or null if <paramref name="screen"/> has no child.</returns> 85 99 public static IScreen GetChildScreen(this IScreen screen) 86 - => runOnRoot(screen, stack => stack.GetChildScreen(screen), () => null); 100 + => getStack(screen)?.GetChildScreen(screen); 87 101 88 102 /// <summary> 89 103 /// Retrieves the parent <see cref="IScreen"/> of an <see cref="IScreen"/>. ··· 91 105 /// <param name="screen">The <see cref="IScreen"/> to retrieve the parent of.</param> 92 106 /// <returns>The parent <see cref="IScreen"/> of <paramref name="screen"/>, or null if <paramref name="screen"/> has no parent.</returns> 93 107 public static IScreen GetParentScreen(this IScreen screen) 94 - => runOnRoot(screen, stack => stack.GetParentScreen(screen), () => null); 108 + => getStack(screen)?.GetParentScreen(screen); 95 109 96 110 internal static Drawable AsDrawable(this IScreen screen) => (Drawable)screen; 97 111 98 - private static void runOnRoot(IDrawable current, Action<ScreenStack> onRoot, Action onFail = null) 112 + private static ScreenStack getStack(IDrawable current) 99 113 { 100 - switch (current) 114 + while (current != null) 101 115 { 102 - case null: 103 - onFail?.Invoke(); 104 - return; 116 + if (current is ScreenStack stack) 117 + return stack; 105 118 106 - case ScreenStack stack: 107 - onRoot(stack); 108 - break; 109 - 110 - default: 111 - runOnRoot(current.Parent, onRoot, onFail); 112 - break; 119 + current = current.Parent; 113 120 } 114 - } 115 121 116 - private static T runOnRoot<T>(IDrawable current, Func<ScreenStack, T> onRoot, Func<T> onFail = null) 117 - { 118 - switch (current) 119 - { 120 - case null: 121 - if (onFail != null) 122 - return onFail.Invoke(); 123 - 124 - return default; 125 - 126 - case ScreenStack stack: 127 - return onRoot(stack); 128 - 129 - default: 130 - return runOnRoot(current.Parent, onRoot, onFail); 131 - } 122 + return null; 132 123 } 133 124 } 134 125 }
+1 -1
osu.Framework/Screens/ScreenStack.cs
··· 30 30 /// <summary> 31 31 /// The currently-active <see cref="IScreen"/>. 32 32 /// </summary> 33 - public IScreen CurrentScreen => stack.FirstOrDefault(); 33 + public IScreen CurrentScreen => stack.Count == 0 ? null : stack.Peek(); 34 34 35 35 private readonly Stack<IScreen> stack = new Stack<IScreen>(); 36 36