抽象类生成器

时间:2018-02-06 10:45:58

标签: c# abstract-class

我在这里失败了。我想重构一部分不使用抽象类的代码。我熟悉json2csharp。这将JSON文件转换为C#类,因此可以轻松地反序列化。

是否有类似的网站/工具接受几个C#类的输入并根据这些类生成基本的抽象类? 这将使重构更容易,因为我不需要创建所有不同的抽象类。

很简单的例子:

输入:

public class TestClass1
{
    public string TestID { get; set; }
    public string TestName { get; set; }
    public int TestValue1 { get; set; }

    public TestClass1()
    {

    }
}

public class TestClass2
{
    public string TestID { get; set; }
    public string TestName { get; set; }
    public int TestValue2 { get; set; }

    public TestClass2()
    {

    }
}

输出:

public abstract class ATestClass
{
    public string TestID { get; set; }
    public string TestName { get; set; }

    protected ATestClass()
    {

    }
}

1 个答案:

答案 0 :(得分:0)

如果使用Roslyn代码分析和代码生成,您可以很快地完成工作。这是一个快速的例子,它是如何工作的。请注意,这在检测公共属性时有点脆弱,因为它基于语法而不是实际语义(使string FooString Foo不兼容的属性)。但是对于实际由另一个代码生成器生成的代码,这应该可以正常工作,因为输入应该是一致的。

var input = @"
    public class TestClass1
    {
        public string TestID { get; set; }
        public string TestName { get; set; }
        public string OtherTest { get; set; }
        public int TestValue1 { get; set; }

        public TestClass1()
        {

        }
    }

    public class TestClass2
    {
        public string TestID { get; set; }
        public string TestName { get; set; }
        public int OtherTest { get; set; }
        public int TestValue2 { get; set; }

        public TestClass2()
        {

        }
    }";

// parse input
var tree = CSharpSyntaxTree.ParseText(input);

// find class declarations and look up properties
var classes = tree.GetCompilationUnitRoot().ChildNodes()
    .OfType<ClassDeclarationSyntax>()
    .Select(cls => (declaration: cls, properties: cls.ChildNodes().OfType<PropertyDeclarationSyntax>().ToDictionary(pd => pd.Identifier.ValueText)))
    .ToList();

// find common property names
var propertySets = classes.Select(x => new HashSet<string>(x.properties.Keys));
var commonPropertyNames = propertySets.First();
foreach (var propertySet in propertySets.Skip(1))
{
    commonPropertyNames.IntersectWith(propertySet);
}

// verify that the property declarations are equivalent
var commonProperties = commonPropertyNames
    .Select(name => (name, prop: classes[0].properties[name]))
    .Where(cp =>
    {
        foreach (var cls in classes)
        {
            // this is not really a good way since this just syntactically compares the values
            if (!cls.properties[cp.name].IsEquivalentTo(cp.prop))
                return false;
        }
        return true;
    }).ToList();

// start code generation
var workspace = new AdhocWorkspace();
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);

// create base class with common properties
var baseClassDeclaration = syntaxGenerator.ClassDeclaration("BaseClass",
    accessibility: Accessibility.Public,
    modifiers: DeclarationModifiers.Abstract,
    members: commonProperties.Select(cp => cp.prop));

var declarations = new List<SyntaxNode> { baseClassDeclaration };

// adjust input class declarations
commonPropertyNames = new HashSet<string>(commonProperties.Select(cp => cp.name));
foreach (var cls in classes)
{
    var propertiesToRemove = cls.properties.Where(prop => commonPropertyNames.Contains(prop.Key)).Select(prop => prop.Value);
    var declaration = cls.declaration.RemoveNodes(propertiesToRemove, SyntaxRemoveOptions.KeepNoTrivia);
    declarations.Add(syntaxGenerator.AddBaseType(declaration, syntaxGenerator.IdentifierName("BaseClass")));
}

// create output
var compilationUnit = syntaxGenerator.CompilationUnit(declarations);
using (var writer = new StringWriter())
{
    compilationUnit.NormalizeWhitespace().WriteTo(writer);
    Console.WriteLine(writer.ToString());
}

这会产生以下输出:

public abstract class BaseClass
{
    public string TestID
    {
        get;
        set;
    }

    public string TestName
    {
        get;
        set;
    }
}

public class TestClass1 : BaseClass
{
    public string OtherTest
    {
        get;
        set;
    }

    public int TestValue1
    {
        get;
        set;
    }

    public TestClass1()
    {
    }
}

public class TestClass2 : BaseClass
{
    public int OtherTest
    {
        get;
        set;
    }

    public int TestValue2
    {
        get;
        set;
    }

    public TestClass2()
    {
    }
}