Фон:
У меня есть атрибут, который указывает, что свойство поля в объекте IsMagic
.У меня также есть класс Magician
, который работает над любым объектом и MakesMagic
, извлекая каждое поле и свойство, IsMagic
, и упаковывая его в оболочку Magic
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MagicTest
{
/// <summary>
/// An attribute that allows us to decorate a class with information that identifies which member is magic.
/// </summary>
[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field, AllowMultiple = false)]
class IsMagic : Attribute { }
public class Magic
{
// Internal data storage
readonly public dynamic value;
#region My ever-growing list of constructors
public Magic(int input) { value = input; }
public Magic(string input) { value = input; }
public Magic(IEnumerable<bool> input) { value = input; }
// ...
#endregion
public bool CanMakeMagicFromType(Type targetType)
{
if (targetType == null) return false;
ConstructorInfo publicConstructor = typeof(Magic).GetConstructor(new[] { targetType });
if (publicConstructor != null) return true; // We can make Magic from this input type!!!
return false;
}
public override string ToString()
{
return value.ToString();
}
}
public static class Magician
{
/// <summary>
/// A method that returns the members of anObject that have been marked with an IsMagic attribute.
/// Each member will be wrapped in Magic.
/// </summary>
/// <param name="anObject"></param>
/// <returns></returns>
public static List<Magic> MakeMagic(object anObject)
{
Type type = anObject?.GetType() ?? null;
if (type == null) return null; // Sanity check
List<Magic> returnList = new List<Magic>();
// Any field or property of the class that IsMagic gets added to the returnList in a Magic wrapper
MemberInfo[] objectMembers = type.GetMembers();
foreach (MemberInfo mi in objectMembers)
{
bool isMagic = (mi.GetCustomAttributes<IsMagic>().Count() > 0);
if (isMagic)
{
dynamic memberValue = null;
if (mi.MemberType == MemberTypes.Property) memberValue = ((PropertyInfo)mi).GetValue(anObject);
else if (mi.MemberType == MemberTypes.Field) memberValue = ((FieldInfo)mi).GetValue(anObject);
if (memberValue == null) continue;
returnList.Add(new Magic(memberValue)); // This could fail at run-time!!!
}
}
return returnList;
}
}
}
Маг может MakeMagic
на anObject
с хотя бы одним полем или свойством, которое IsMagic
для создания общего List
из Magic
, например так:
using System;
using System.Collections.Generic;
namespace MagicTest
{
class Program
{
class Mundane
{
[IsMagic] public string foo;
[IsMagic] public int feep;
public float zorp; // If this [IsMagic], we'll have a run-time error
}
static void Main(string[] args)
{
Mundane anObject = new Mundane
{
foo = "this is foo",
feep = -10,
zorp = 1.3f
};
Console.WriteLine("Magic:");
List<Magic> myMagics = Magician.MakeMagic(anObject);
foreach (Magic aMagic in myMagics) Console.WriteLine(" {0}",aMagic.ToString());
Console.WriteLine("More Magic: {0}", new Magic("this works!"));
//Console.WriteLine("More Magic: {0}", new Magic(Mundane)); // build-time error!
Console.WriteLine("\nPress Enter to continue");
Console.ReadLine();
}
}
}
Обратите внимание, что Magic
оболочки могут только идтивокруг свойств или полей определенных типов.Это означает, что только свойство или поле, которые содержат данные определенных типов, должны быть помечены как IsMagic
.Чтобы усложнить задачу, я ожидаю, что список конкретных типов будет меняться по мере развития потребностей бизнеса (поскольку программирование Magic так востребовано).
Хорошая новость заключается в том, что Magic
имеет некоторую безопасность во время сборки,Если я попытаюсь добавить код вроде new Magic(true)
, Visual Studio скажет мне, что это неправильно, поскольку для Magic
нет конструктора, который бы занимал bool
.Существует также некоторая проверка во время выполнения, так как метод Magic.CanMakeMagicFromType
может использоваться для обнаружения проблем с динамическими переменными.
Описание проблемы:
Плохие новостичто нет проверки во время сборки атрибута IsMagic
.Я могу с радостью сказать поле Dictionary<string,bool>
в каком-то классе IsMagic
, и мне не скажут, что это проблема до времени выполнения.Хуже того, пользователи моего магического кода будут создавать свои собственные мирские классы и украшать свои свойства и поля атрибутом IsMagic
.Я хотел бы помочь им увидеть проблемы до того, как они станут проблемами.
Предлагаемое решение:
В идеале я мог бы установить какой-нибудь флаг AttributeUsage на свой IsMagic
атрибут для указания Visual Studio использовать метод Magic.CanMakeMagicFromType()
для проверки свойства или типа поля, к которому присоединяется атрибут IsMagic
.К сожалению, такого атрибута, по-видимому, нет.
Однако, похоже, что можно использовать Roslyn для отображения ошибки, когда IsMagic
помещается в поле или свойство, которое имеет Type
, которые нельзя обернуть в Magic
.
Где мне нужна помощь:
У меня проблемы с проектированием анализатора Roslyn.Суть проблемы в том, что Magic.CanMakeMagicFromType
принимает System.Type
, но Рослин использует ITypeSymbol
для представления типов объектов.
Идеальный анализатор будет:
- Не требует от менявести список разрешенных типов, которые можно заключить в
Magic
.В конце концов, Magic
имеет список конструкторов, которые служат этой цели. - Разрешить естественное приведение типов.Например, если
Magic
имеет конструктор, который принимает IEnumerable<bool>
, то Roslyn должен разрешить IsMagic
присоединяться к свойству с типом List<bool>
или bool[]
.Этот каст Магии имеет решающее значение для функциональности Мага.
Буду признателен за любые указания о том, как кодировать анализатор Roslyn, который «знает» о конструкторах в Magic
.