/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace dnSpy.Debugger.DotNet.Metadata { /// /// Base class of types, members, assemblies, modules that allows you to attach data to instances /// public abstract class DmdObject { readonly object lockObj; List<(RuntimeTypeHandle key, object data)>? dataList; /// /// Gets the lock object used by this instance /// protected object LockObject => lockObj; /// /// Constructor /// protected DmdObject() => lockObj = new object(); /// /// Checks if the data exists or is null /// /// Type of data /// public bool HasData() where T : class => TryGetData(out var value); /// /// Gets or creates data /// /// Type of data /// public T GetOrCreateData() where T : class, new() => GetOrCreateData(() => new T()); /// /// Gets data /// /// Type of data /// Result /// public bool TryGetData([NotNullWhen(true)] out T? value) where T : class { lock (lockObj) { if (dataList is not null) { var type = typeof(T).TypeHandle; foreach (var kv in dataList) { if (kv.key.Equals(type)) { value = (T)kv.data; return true; } } } } value = null; return false; } /// /// Gets existing data or throws if the data doesn't exist /// /// Type of data /// public T GetData() where T : class { if (TryGetData(out var data)) return data; throw new InvalidOperationException(); } /// /// Gets or creates data /// /// Type of data /// Creates the data if it doesn't exist /// public T GetOrCreateData(Func create) where T : class { if (create is null) throw new ArgumentNullException(nameof(create)); lock (lockObj) { if (dataList is null) dataList = new List<(RuntimeTypeHandle, object)>(); var type = typeof(T).TypeHandle; foreach (var kv in dataList) { if (kv.key.Equals(type)) return (T)kv.data; } var value = create(); Debug.Assert(!(value is DmdObject)); dataList.Add((type, value)); return value; } } } }