Я работал над атрибутами и хотел ограничить наследование во время компиляции. Это не сработало, однако я нашел решение для времени выполнения. Я держал это как компактный, но законченный насколько возможно, так что вот код для любого, кто, оказывается, заинтересован. Использование простое: вы добавляете атрибут с заданными типами, которым разрешено наследовать от него.
Атрибут:
[AttributeUsage (AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
sealed class RestrictedInheritabilityAttribute : Attribute
{
public List<Type> AllowedTypes = new List<Type> ();
public RestrictedInheritabilityAttribute (Type allowed)
{
AllowedTypes.Add (allowed);
}
public RestrictedInheritabilityAttribute (params Type[] allowed)
{
AllowedTypes.AddRange (allowed);
}
public bool CheckForException (Type primary)
{
foreach (Type t in AllowedTypes)
{
if (primary == t) return true;
}
throw new RestrictedInheritanceException (primary);
}
}
Пользовательское исключение:
public class RestrictedInheritanceException : ApplicationException
{
public RestrictedInheritanceException (Type t) : base ("The inheritance of '" + t.BaseType.FullName + "' is restricted. " + t.FullName + " may not inherit from it!" + Environment.NewLine) { }
}
Теперь, один из способов - встроить строки кода в конструктор класса, который имеет атрибут, но это не самый красноречивый способ его решения. Вместо этого вы можете запустить следующий метод на ранней стадии выполнения, чтобы вы могли выполнить проверки и выдать исключение (или просто написать простое сообщение).
void RunAttributes ()
{
try
{
Type[] allTypes = Assembly.GetExecutingAssembly ().GetTypes ();
foreach (Type t in allTypes)
{
Type baseType = t.BaseType;
if (baseType != null && baseType != typeof (object))
{
var a = baseType.GetCustomAttribute<RestrictedInheritabilityAttribute> ();
if (a != null) a.CheckForException (t);
}
}
}
catch (Exception e)
{
throw e;
}
}
Использование:
//[RestrictedInheritability (typeof (MidClass1), typeof (MidClass2))]
[RestrictedInheritability (typeof (MidClass1))]
class BaseClass { }
class MidClass1 : BaseClass { } // Is fine to go.
class MidClass2 : BaseClass { } // Will throw exception.
// These are not checked, unless you set 'Inherited = true', then you have to
// declare every single class inheriting from the 'root' which has the attribute.
class SubClass1 : MidClass1 { }
class SubClass2 : MidClass2 { }
Когда вы запускаете вашу программу (и в идеале запускаете метод RunAttributes раньше), пользовательское исключение будет выдано, чтобы указать, что произошло запрещенное наследование. Лично я мог бы использовать это, чтобы запретить моим коллегам наследовать от классов, которые не должны наследоваться из-за того, что они предназначены для низкоуровневого кода, который является основой всего рабочего проекта.
В любом случае этот код может дать представление о рабочем примере не слишком простого атрибута. Больше всего я нашел только атрибуты, содержащие некоторые элементы, которые затем вызывались практически без контекста.
Редактирование: для пользователей Unity - найдите атрибут UnityEditor.Callbacks.DidReloadScripts в методе static , чтобы вы могли запускать сценарий после компиляции, а не во время выполнения, это то, что я лично хотел иметь.