diff --git a/Data/Result.cs b/Data/Result.cs
new file mode 100644
index 00000000..2c582723
--- /dev/null
+++ b/Data/Result.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace TweetDuck.Data{
+    sealed class Result<T>{
+        public bool HasValue => exception == null;
+
+        public T Value => HasValue ? value : throw new InvalidOperationException("Requested value from a failed result.");
+        public Exception Exception => exception ?? throw new InvalidOperationException("Requested exception from a successful result.");
+
+        private readonly T value;
+        private readonly Exception exception;
+
+        public Result(T value){
+            this.value = value;
+            this.exception = null;
+        }
+
+        public Result(Exception exception){
+            this.value = default(T);
+            this.exception = exception ?? throw new ArgumentNullException(nameof(exception));
+        }
+
+        public void Handle(Action<T> onSuccess, Action<Exception> onException){
+            if (HasValue){
+                onSuccess(value);
+            }
+            else{
+                onException(exception);
+            }
+        }
+
+        public Result<R> Select<R>(Func<T, R> map){
+            return HasValue ? new Result<R>(map(value)) : new Result<R>(exception);
+        }
+    }
+}
diff --git a/TweetDuck.csproj b/TweetDuck.csproj
index 27df5cbd..fd09944d 100644
--- a/TweetDuck.csproj
+++ b/TweetDuck.csproj
@@ -259,6 +259,7 @@
     </Compile>
     <Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
     <Compile Include="Data\ResourceLink.cs" />
+    <Compile Include="Data\Result.cs" />
     <Compile Include="Data\Serialization\FileSerializer.cs" />
     <Compile Include="Data\InjectedHTML.cs" />
     <Compile Include="Data\Serialization\ITypeConverter.cs" />