Roslyn для автоматического перевода кода C# в 1С-код

  • Tutorial
Появилась идея посмотреть, как будет выглядеть объектно-ориентированный подход в 1С, язык которой очень ограничен в средствах и не предусматривает определение классов. Программа по автоматическому переводу определений классов C# в другой язык позволила бы менять генерируемый код по мере появления новых идей. Поиски средств реализации привели к проекту Roslyn – открытому компилятору C#.

Roslyn – это открытая платформа компиляции C# и Visual Basic. Roslyn выполняет два основных действия: строит синтаксическое дерево (парсинг) и компилирует синтаксическое дерево. Дополнительно позволяет анализировать исходный код, рекурсивно обходить его, работать с проектами Visual Studio, выполнять код на лету.

Обратите внимание, что на данный момент Roslyn в стадии Бета. Исходя из этого, со временем в компиляторе может что-то поменяться.


Roslyn – открытый компилятор C#



Подключить Roslyn в проект можно через Nuget:
Install-Package Microsoft.CodeAnalysis –Pre

Для удобства в коде лучше сразу подключить три пространства имен
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;

Получить синтаксическое дерево кода из строки (или файла) можно так:
SyntaxTree tree = CSharpSyntaxTree.ParseText(codeString);

Синтаксическое дерево представляет из себя иерархию объектов, наследованных от SyntaxNode. Объекты созданы на все случаи жизни. Примеры: ClassDeclarationSyntax — определение класса, NamespaceDeclarationSyntax – определение пространства имен, PropertyDeclarationSyntax – определение свойства, AccessorDeclarationSyntax – определение метода доступа к свойству (get/set), BlockSyntax – содержимое блока (между фигурными скобками), ExpressionStatementSyntax – выражение и т.д.

Если есть задача рекурсивно пройти все элементы дерева, можно создать свой класс Walker и наследовать его от CSharpSyntaxWalker. Базовый класс позволяет переопределять общий метод Visit(SyntaxNode node) или большое множество специализированных, вида: void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node), void VisitClassDeclaration(ClassDeclarationSyntax node), void VisitConstructorDeclaration(ConstructorDeclarationSyntax node) и т.д. Не забывайте вызвать в каждом переопределенном методе базовый метод, чтобы не останавливать рекурсии.

Вызов рекурсивного обхода можно запустить следующим образом:
var walker = new Walker();
walker.Visit(tree.GetRoot());

В синтаксическом дереве нет информации о типах. Информация об используемых типах появляется после вызова:
var compilation = CSharpCompilation.Create("1ccode").WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).AddReferences(new MetadataFileReference(typeof(object).Assembly.Location)).AddSyntaxTrees(tree);
var Model = compilation.GetSemanticModel(tree);

После этого вызова можно получать информацию об используемых типах, вызывая, например для класса
var classSymbol = Model.GetDeclaredSymbol(classDeclarationSyntax);

Теперь, имея информацию, о типе, можно узнать какой класс унаследовал данный тип:
var type = type.BaseType;

Или получить все члены типа через type.GetMembers()

Автоматический перевод кода C# в код 1С



Код не претендует на полноту и правильность, так как имеет цель получить общее представление об ООП-подходе в 1С.

Для перевода C#-кода в код 1С был создан класс Walker, наследованный от CSharpSyntaxWalker. Walker перебирает все определения и строит на выходе 1С-код.

Класс производит следующие преобразования.

Пространство имен переводится методом VisitNamespaceDeclaration в модуль 1С, где точки в названии заменены на знаки подчеркивания.

Понятия класс в 1С нет, поэтому определение класса в методе VisitClassDeclaration пропускается. Имя класса будет присутствовать в названии каждой функции и процедуры 1С, чтобы обозначить принадлежность к одному типу. Присутствующие в базовых классах методы, но отсутствующие в текущем классе через DeclareBaseClassMethodsToImplement и DeclareBaseClassPropertiesToImplement определяются с вызовом «базовых» функций/процедур 1С.

Конструкторы в VisitConstructorDeclaration переводятся в определения функций 1С с именем класса, первым параметром _this и списком параметров. Если нет вызова другого конструктора этого класса, происходит инициализация всех полей класса в структуре. Определяется вызов других конструкторов.

Определение свойств в VisitPropertyDeclaration пропускаются. Важны определения их методов доступа.

Методы доступа свойств в VisitAccessorDeclaration переводятся в определения с именами <название класса>_Получить_<имя свойства> и <название класса>_Установить_<имя свойства>. Если они авто-реализованные (auto-implemented), то генерируется код доступа к переменной _this._private_<название класса>_<имя свойства>.

Для методов в VisitMethodDeclaration генерируются определения 1С-процедур.

Выражения и «возвраты» в VisitExpressionStatement и VisitReturnStatement комментируются через // и вставляются в текст как есть.

Исходный код Walker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;


namespace Roslyn
{
    public class Walker : CSharpSyntaxWalker
    {

        SyntaxTree Tree { get; set; }
        CSharpCompilation Compilation { get; set; }
        SemanticModel Model { get; set; }
        TextWriter Writer { get; set; }
        public Walker(TextWriter writer, SyntaxTree tree, CSharpCompilation compilation) : base()
        {
            Writer = writer;
            Tree = tree;
            Compilation = compilation;
            Model = Compilation.GetSemanticModel(tree);
        }

        Dictionary<ClassDeclarationSyntax, FieldDeclarationSyntax[]> _classFields = new Dictionary<ClassDeclarationSyntax, FieldDeclarationSyntax[]>();

        NamespaceDeclarationSyntax _currentNamespace;
        ClassDeclarationSyntax _currentClass;
        PropertyDeclarationSyntax _currentProperty;

        private int Tabs = 0;
        public override void Visit(SyntaxNode node)
        {
            //Tabs++;
            //var indents = new String('\t', Tabs);
            //Writer.WriteLine(indents + node.GetType().Name + "/" + node.CSharpKind());
            base.Visit(node);
            //Tabs--;

        }

        public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
        {
            _currentNamespace = node;

            Writer.WriteLine("Модуль " + node.Name.ToString().Replace(".", "_"));
            base.VisitNamespaceDeclaration(node);
        }

        public override void VisitClassDeclaration(ClassDeclarationSyntax node)
        {
            _currentClass = node;

            var fields = node.ChildNodes().OfType<FieldDeclarationSyntax>().ToArray();
            _classFields[node] = fields;

            Writer.WriteLine();
            Writer.WriteLine(string.Format("//Класс {0}", node.Identifier));
            base.VisitClassDeclaration(node);

            DeclareBaseClassPropertiesToImplement(node);
            DeclareBaseClassMethodsToImplement(node);

        }

        void DeclareBaseClassMethodsToImplement(ClassDeclarationSyntax classNode)
        {
            var classSymbol = Model.GetDeclaredSymbol(classNode);

            List<string> processedMembers = new List<string>();
            var type = classSymbol;
            while (type != null)
            {
                foreach(var member in type.GetMembers())
                {
                    var declarators = member.DeclaringSyntaxReferences;
                    if (declarators == null || declarators.Length == 0)
                        continue;
                    if (declarators.Length != 1)
                        throw new NotImplementedException();

                    var memberNode = declarators[0].GetSyntax() as MethodDeclarationSyntax;
                    if (memberNode == null)
                        continue;

                    if (processedMembers.Any(m=>m == member.Name))
                        continue;
                    processedMembers.Add(member.Name);

                    if (type == classSymbol)
                        //Skip original class members. Declare only base classes
                        continue;

                    Writer.WriteLine();
                    Writer.WriteLine(string.Format("Процедура {0}_{1}(_this)", _currentClass.Identifier, memberNode.Identifier));
                    Writer.WriteLine(string.Format("    {0}_{1}(_this);", type.Name, member.Name));
                    Writer.WriteLine(string.Format("КонецПроцедуры;"));
                }
                type = type.BaseType;
            }
        }

        void DeclareBaseClassPropertiesToImplement(ClassDeclarationSyntax classNode)
        {
            var classSymbol = Model.GetDeclaredSymbol(classNode);

            List<string> processedMembers = new List<string>();
            var type = classSymbol;
            while (type != null)
            {
                foreach(var member in type.GetMembers())
                {
                    var declarators = member.DeclaringSyntaxReferences;
                    if (declarators == null || declarators.Length == 0)
                        continue;
                    if (declarators.Length != 1)
                        throw new NotImplementedException();

                    var memberNode = declarators[0].GetSyntax() as PropertyDeclarationSyntax;
                    if (memberNode == null)
                        continue;

                    if (processedMembers.Any(m => m == memberNode.Identifier.ToString()))
                        continue;
                    processedMembers.Add(memberNode.Identifier.ToString());

                    if (type == classSymbol)
                        //Skip original class members. Declare only base classes
                        continue;

                    Writer.WriteLine();
                    Writer.WriteLine(string.Format("Функция {0}_Получить_{1}(_this)", _currentClass.Identifier, memberNode.Identifier));
                    Writer.WriteLine(string.Format("    Возврат {0}_Получить_{1}(_this);", type.Name, member.Name));
                    Writer.WriteLine(string.Format("КонецФункции;"));

                    Writer.WriteLine();
                    Writer.WriteLine(string.Format("Процедура {0}_Установить_{1}(_this, value)", _currentClass.Identifier, memberNode.Identifier));
                    Writer.WriteLine(string.Format("    {0}_Установить_{1}(_this);", type.Name, member.Name));
                    Writer.WriteLine(string.Format("КонецПроцедуры;"));
                }
                type = type.BaseType;
            }
        }

        public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
        {
            Writer.WriteLine();

            var symbol = Model.GetDeclaredSymbol(node);

            List<string> parameters = new List<string>();
            parameters.Add("_this");
            parameters.AddRange(node.ParameterList.Parameters.Select(m => m.Identifier.ToString()).ToArray());

            Writer.WriteLine(string.Format("Функция {0}({1}){2}", node.Identifier, String.Join(", ", parameters), " Экспорт"));
            Writer.WriteLine();

            Tabs++;
            var indents = new String('\t', Tabs);

            //Initialize members first if no this constructor initializer (:this()) call
            if (!node.DescendantNodes().OfType<ConstructorInitializerSyntax>().Any(m=>m.CSharpKind() == SyntaxKind.ThisConstructorInitializer) && _classFields.ContainsKey(_currentClass))
            {
                Writer.WriteLine(indents + String.Format("//Инициализация полей"));
                //Writer.WriteLine(String.Format("_this = Новый Структура();"));
                foreach (var field in _classFields[_currentClass])
                {
                    Writer.WriteLine(String.Format(indents + "_this.Вставить(\"{0}\", {1})", field.Declaration.Variables[0].Identifier, field.Declaration.Variables[0].Initializer.Value));
                }
            }

            if (node.Initializer != null)
            {
                List<string> arguments = new List<string>();
                arguments.Add("_this");
                arguments.AddRange(node.Initializer.ArgumentList.Arguments.Select(m => m.Expression.ToString()).ToArray());

                if (node.Initializer.ThisOrBaseKeyword.CSharpKind() == SyntaxKind.BaseKeyword)
                {
                    Writer.WriteLine(indents + String.Format("//Вызов конструктора базового класса"));
                    Writer.WriteLine(indents + String.Format("{0}({1});", _currentClass.BaseList.Types[0], String.Join(", ", arguments)));
                }
                else if (node.Initializer.CSharpKind() == SyntaxKind.ThisConstructorInitializer)
                {
                    Writer.WriteLine(indents + String.Format("//Вызов другого конструктора"));
                    Writer.WriteLine(indents + String.Format("{0}({1});", _currentClass.Identifier, String.Join(", ", arguments)));
                }
            }

            Writer.WriteLine(String.Format(indents + "_this.Вставить(\"__type\", \"{0}.{1}\")", symbol.ContainingNamespace.Name, symbol.ContainingType.Name));

            base.VisitConstructorDeclaration(node);
            Tabs--;

            Writer.WriteLine(indents + string.Format("Возврат _this;"));
            Writer.WriteLine(string.Format("КонецФункции; //{0}({1}){2}", node.Identifier, String.Join(", ", parameters), " Экспорт"));
        }

        public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
        {
            _currentProperty = node;

            var symbol = Model.GetDeclaredSymbol(node);

            base.VisitPropertyDeclaration(node);
        }
        public override void VisitAccessorDeclaration(AccessorDeclarationSyntax node)
        {
            Writer.WriteLine();
            if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration)
            {
                Writer.WriteLine(string.Format("Функция {0}_Получить_{1}(_this)", _currentClass.Identifier, _currentProperty.Identifier));
            }
            else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration)
            {
                Writer.WriteLine(string.Format("Процедура {0}_Установить_{1}(_this, value)", _currentClass.Identifier, _currentProperty.Identifier));
            }

            Tabs++;

            if (node.Body == null)
            {
                //auto implemented
                var indents = new String('\t', Tabs);
                if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration)
                {
                    Writer.WriteLine(indents + string.Format("Возврат _this._private_{0}_{1};", _currentClass.Identifier, _currentProperty.Identifier));
                }
                else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration)
                {
                    Writer.WriteLine(indents + string.Format("_this._private_{0}_{1} = value;", _currentClass.Identifier, _currentProperty.Identifier));
                }
            }

            base.VisitAccessorDeclaration(node);
            Tabs--;

            if (node.CSharpKind() == SyntaxKind.GetAccessorDeclaration)
            {
                Writer.WriteLine(string.Format("КонецФункции;"));
            }
            else if (node.CSharpKind() == SyntaxKind.SetAccessorDeclaration)
            {
                Writer.WriteLine(string.Format("КонецПроцедуры;"));
            }
        }

        public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            Writer.WriteLine();
            Writer.WriteLine(string.Format("Процедура {0}_{1}(_this)", _currentClass.Identifier, node.Identifier));

            Tabs++;
            base.VisitMethodDeclaration(node);
            Tabs--;

            Writer.WriteLine(string.Format("КонецПроцедуры;"));
        }

        public override void VisitExpressionStatement(ExpressionStatementSyntax node)
        {
            var indents = new String('\t', Tabs);
            Writer.WriteLine(("\r\n" + node.ToString()).Replace("\r\n", "\r\n" + indents + "//"));
            base.VisitExpressionStatement(node);
        }
        public override void VisitReturnStatement(ReturnStatementSyntax node)
        {
            var indents = new String('\t', Tabs);
            Writer.WriteLine(("\r\n" + node.ToString()).Replace("\r\n", "\r\n" + indents + "//"));
            base.VisitReturnStatement(node);
        }
        //public override void VisitBlock(BlockSyntax node)
        //{
        //    Writer.WriteLine(node.ToString());
        //    base.VisitBlock(node);
        //}
    }
}



Результат работы



В итоге код

Исходный код на C#
namespace ПространствоИмен1.ПИ2
{
    public class А
    {
        public А()
        {
            Свойство1 = "Конструктор А";
        }

        private int _поле1 = 10;        
        public int Поле1 {get {return _поле1;} set {_поле1 = value;}}        

        public string Свойство1 {get; set;}
        public void Метод1()
        {
            Свойство1 = "Метод1";
        }
    }

    public class Б : А
    {
        private int _поле1 = 20;        

        public Б() : base()
        {
            Свойство1 = "Конструктор Б";
            Метод1();
        }

        public Б(int i) : this()
        {
            Свойство1 = "Конструктор Б(int i)";
            Метод1();
        }
    }
}    



Будет переведен в код 1С: Предприятие

Исходный код 1С:Предприятие
Модуль ПространствоИмен1_ПИ2

//Класс А

Функция А(_this) Экспорт

	//Инициализация полей
	_this.Вставить("_поле1", 10)
	_this.Вставить("__type", "ПИ2.А")

	//Свойство1 = "Конструктор А";
	Возврат _this;
КонецФункции; //А(_this) Экспорт

Функция А_Получить_Поле1(_this)

	//return _поле1;
КонецФункции;

Процедура А_Установить_Поле1(_this, value)

	//_поле1 = value;
КонецПроцедуры;

Функция А_Получить_Свойство1(_this)
	Возврат _this._private_А_Свойство1;
КонецФункции;

Процедура А_Установить_Свойство1(_this, value)
	_this._private_А_Свойство1 = value;
КонецПроцедуры;

Процедура А_Метод1(_this)

	//Свойство1 = "Метод1";
КонецПроцедуры;

//Класс Б

Функция Б(_this) Экспорт

	//Инициализация полей
	_this.Вставить("_поле1", 20)
	//Вызов конструктора базового класса
	А(_this);
	_this.Вставить("__type", "ПИ2.Б")

	//Свойство1 = "Конструктор Б";

	//Метод1();
	Возврат _this;
КонецФункции; //Б(_this) Экспорт

Функция Б(_this, i) Экспорт

	//Вызов другого конструктора
	Б(_this);
	_this.Вставить("__type", "ПИ2.Б")

	//Свойство1 = "Конструктор Б(int i)";

	//Метод1();
	Возврат _this;
КонецФункции; //Б(_this, i) Экспорт

Функция Б_Получить_Поле1(_this)
    Возврат А_Получить_Поле1(_this);
КонецФункции;

Процедура Б_Установить_Поле1(_this, value)
    А_Установить_Поле1(_this);
КонецПроцедуры;

Функция Б_Получить_Свойство1(_this)
    Возврат А_Получить_Свойство1(_this);
КонецФункции;

Процедура Б_Установить_Свойство1(_this, value)
    А_Установить_Свойство1(_this);
КонецПроцедуры;

Процедура Б_Метод1(_this)
    А_Метод1(_this);
КонецПроцедуры;

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 22

    +5
    Смесь русского и английского режет глаза.
     _this.Вставить("__type", "ПИ2.Б")
    
      +1
      да, вы правы. Не понятно, что с этим делать. Если заменить на
      _это.Вставить("__тип", "ПИ2.Б")
      

      то не очень подходит для англоязычных конфигураций
        +3
        Не ясно почему русский, 8.х отлично везде английский поддерживает, даже при именовании справочников и прочей шелухи :)
          0
          Предлагаю как вариант, подключиться через GoogleTranslate API и переводить слова. Можно еще самому словарь набивать. Через год слов станет столько, что можно будет уже и не париться =)
        +2
        Странно все это, и не понятно зачем нужно такое.

        В 1С уже все прикладные абстрактные классы есть, другое дело, что 90% программистов 1С не понимают как их использовать правильно =)

        Да, с множественным наследованием тут не поиграться, но и задач, в которых оно было бы нужно — единицы, и почти все связаны с описанием схем сериализации, а как раз там (XDTO) множественное наследование можно и нужно использовать.

        Что есть класс в 1С? Это объект метаданных, будь то Справочник, план обмена, документ, обработка или регистр расчета, всех их объединяет одно: У них есть реквизиты и табличные части — суть свойства класса, есть модуль объекта — суть методы класса, и модуль менеджера — суть статические методы класса.

        Хочешь красиво и правильно это использовать? Ну так пожалуйста: Делаешь обработку, в реквизитах описываешь все свойства и их типы, в модуле объекта все методы и паблик методы помечаешь как экспортные. В таком виде уже можно использовать обработку как класс — я надеюсь все умеют программно создавать экземпляр обработки? =)
          0
          Мне был интересен ООП-подход в контексте модульности 1С. Можно ли разделить большое монолитное приложение 1С на малосвязанные модули. Что мне для этого не хватает из C#/Java: классы, наследование классов от какого-нибудь класса Module (может, интерфейсы IModule), атрибуты вида [Module], получение метаданных класса через GetType(), события, проверка типов «o is Module».

          Как оказалось, у подхода компилирования C# в код 1С могут быть еще достоинства. Разработка 1С из конфигуратора в теории может быть перемещена в Visual Studio со всеми преимуществами: intellisense, быстрый рефакторинг, контроль типов, встроенный контроль ошибок при компилировнии. Плюс контроль версий сторонними средствами (проект C# в виде файлов в папке). Возможно, юнит-тестирование. Еще код на C# более выразительный. Сложный проект можно разделить на модули-сборки проектов C#.
            0
            Разрабатывать свои интерфейсы — это очень интересно, конечно, но даже если мы возьмем тот же .NET, все базовые интерфейсы уже реализованы, достаточно их выучить и научиться имплементировать правильно, так же и в 1С, уже есть все базывые интерфейсы, необходимости разработки чего-то своего нет. Множественность наследования — по сути, так же не нужна. (Приведите пример, когда в 1С она нужна?) События есть, как события объектов, так и события форм, события статических метаданных, так же как и гобальные подписки на события. Проверка типов есть ТипЗнч(Источник) говорит какой именно тим имеет объект Источник, сравнить его можно с типом полученным по конструктору Тип(«ПеречислениеСсылка.ВидыШтрихкодов») или Тип(«ДокументОбъект.РасходнаяНакладная») используется всегда в глобальных подписках на события в не типовых доработках, не снимающих с поддержки типовую конфигурацию.

            Компилирование в C# аналогично компилированию в 1С, так же как C# производит конвертацию в MSIL 1С производит компиляцию в свой промежуточный язык исполнения, который далее исполняется либо в CLR для C# либо в Платформе для 1С. Intellisense в 1С 8.3 есть, подсказки глобальных функций, подсказки реквизитов определенных типов, подсказки по объектам метаданных, так же все функции имеющие стандарт комментировая так же подсказываются с пояснениями что за аргумент и какого он типа при наборе. Надо просто научиться всем этим пользоваться =)




            Для контроля версий есть Хранилище конфигурации, афигенно удобный инстурмент, в котором можно захватывать и помещать объекты общей конфигурации, чтобы не было коллизий между группой разработчиков. Но можно так же и обычный Git использовать, достаточно выгрузить конфигурацию в промежуточные файды (txt, xml, mxl, epf, ert), но именно для 1С это менее удобно, чем использовать хранилище. В 8.3 есть unit тестирование, ровно как и нагрузочное тестирование, как и оценка производительности в «особых» условиях. Любой код можно разделить на смысловые блоки, абстрагирование — это основная задача программиста. Всегда можно вынести общие модули без контекста, и стандартизировать их.
              0
              Теперь давайте разберем все ваши высказывания без фанатизма и без маркетинговых картинок. Тем более, что я бы постеснялся показывать комментарии перед определениями функций – весь мир давно уже перешел на XmlDoc. Хотелось бы и у 1С видеть что-то на ее манер в виде ///<описание></описание> <параметр></параметр>

              Разрабатывать свои интерфейсы — это очень интересно, конечно, но даже если мы возьмем тот же .NET, все базовые интерфейсы уже реализованы, достаточно их выучить и научиться имплементировать правильно, так же и в 1С, уже есть все базывые интерфейсы, необходимости разработки чего-то своего нет. Множественность наследования — по сути, так же не нужна. (Приведите пример, когда в 1С она нужна?) События есть, как события объектов, так и события форм, события статических метаданных, так же как и гобальные подписки на события. Проверка типов есть ТипЗнч(Источник) говорит какой именно тим имеет объект Источник, сравнить его можно с типом полученным по конструктору Тип(«ПеречислениеСсылка.ВидыШтрихкодов») или Тип(«ДокументОбъект.РасходнаяНакладная») используется всегда в глобальных подписках на события в не типовых доработках, не снимающих с поддержки типовую конфигурацию.


              Все базовые классы и интерфейсы в .Net конечно разработаны, но, тем не менее, .Net предполагает определять классы. А если бы наследования не было в .Net, использовать в статье класс Walker было бы невозможно, и решение задачи заняло бы раза в 3 больше времени (организация рекурсии, приведение типов). С чего вы взяли, что множественность наследования не нужна? Странно мыслите. В близких аналогах 1С: SAP и Axapta нужна, а в 1С – оказывается не нужна. Если ее нет в 1С, то это не значит, что она не нужна. Это значит, что 1С в меру своей ограниченности не может предоставить такую возможность. Соответственно и ТипЗнч, Тип – совершенно бесполезны, потому что они работают только с предопределенными классами.
                0
                Представьте, что Справочники — это интерфейс, а Контрагенты, Номенклатура, ВидыКонтактнойИнформации — это Классы.
                Документы — это интерфейс, а РасходнаяНакладная, КассовыйОрдер — это классы.
                Регистры бухгалтерии — это интерфейс — а Хозрассчетный — это класс.
                Обработки — это интерфейс — а ЗагрузкаНоменклатурыИзExcel — класс
                и т.д.

                Я же говорю — все нужные интерфейсы уже есть, только клепай свои классы и все.

                Сможете назвать интерфейс, которого из всех предложенных 1С`ом не хватает Вам?
                  0
                  Вы мне предлагаете воображением компенсировать ограниченность языка 1С?
                    0
                    Нет я предлагаю, всего лишь научиться использовать язык до того, как его осуждать =)
                      0
                      Прежде, чем советовать «научиться», вам стоило бы знать, что определение перечисленных вами основных объектов выполняется не через язык 1С, а настройками конфигуратора с возможностью определения некоторых методов в коде 1С. Соответственно «использовать язык» для определения не получится, так как в языке 1С не предусмотрено таких конструкций определения.

                      Мне не хватает определения класса модулей из задачи «Модульное приложение на 1С» (http://infostart.ru/public/192074/). Мешают в БСП через один модули с пометкой Переопределяемый или процедуры, например, ПриДобавленииСлужебныхСобытий, что эквивалентно override-методам. Не хватает явного указания через поддерживаемый Интерфейс, что часть справочников/документов поддерживают особую работу с групповой обработкой (БСП обошла этот момент вызовом в попытка-исключение функций РеквизитыРедактируемыеВГрупповойОбработке, РеквизитыНеРедактируемыеВГрупповойОбработке для каждого справочника/документа).
                0
                Компилирование в C# аналогично компилированию в 1С, так же как C# производит конвертацию в MSIL 1С производит компиляцию в свой промежуточный язык исполнения, который далее исполняется либо в CLR для C# либо в Платформе для 1С.

                Чудесным образом Ассемблер тоже выполняет компилирование в машинный код, но им редко пользуются, предпочитая C++ и C# из-за более выразительных средств языка. CIL с оп-кодом 1С я бы не сравнивал из-за огромной убогости последнего, где из 128 кодов коды с 51 по 122 посвящены вызовам функций вида Min, Max, BeginOfDay, BeginOfMonth, BeginOfYear и т.д. (странно, что вызова BeginOf100Years нет :)).

                ОП-коды 1С
                        Nop = 0,
                        LineNo = 1,
                        PushStatic = 2,
                        PushLocal = 3,
                        PushConst = 4,
                        PushReturn = 5,
                        PushFalse = 6,
                        PushTrue = 7,
                        PushUndefined = 8,
                        PushNull = 9,
                        PushEmpty = 10,//???
                        Pop = 11,
                        GetObjectProperty = 12,
                        GetIndexed = 13,
                        SelEnum = 14,
                        NextEnum = 15,
                        Assign = 16, //Pop = Pop
                
                        AssignReturnValue = 17, //Pop = Pop
                        SetParamsCount = 18, //Parameter number
                        Call = 19,
                        CallObjectProcedure = 20,
                        CallObjectFunction = 21,
                        Ret = 22,
                
                        Neg = 23,
                        Add = 24,
                        Sub = 25,
                        Mul = 26,
                        Div = 27,
                        Mod = 28,
                
                        Not = 29,
                        AndJmp = 30,
                        OrJmp = 31,
                        LogVal = 32, //???
                        EQ = 33,
                        NE = 34,
                        GT = 35,
                        LT = 36,
                        GE = 37,
                        LE = 38,
                
                        Jmp = 39,
                        JZ = 40, //Else go to op1
                        JNZ = 41,
                        JmpLbl = 42,
                
                        Inc = 43,
                
                        PushTry = 44,
                        PopTry = 45,
                        EndTry = 46,
                        Raise = 47,
                
                        PushFor = 48,
                        PushForVar = 49,
                        PopFor = 50,
                
                        New = 51, //Constants[op1]
                
                        Execute = 52,
                        StrLen = 53,
                        TrimL = 54,
                        TrimR = 55,
                        TrimAll = 56,
                        Left = 57,
                        Right = 58,
                        Mid = 59,
                        Find = 60,
                        Upper = 61,
                        Lower = 62,
                        Char = 63,
                        CharCode = 64,
                        IsBlankStr = 65,
                        Int = 66,
                        RoundDefault = 67,
                        Round = 68,
                
                        Boolean = 69,
                        Numeric = 70,
                        String = 71,
                
                        Date = 72,
                        Date3 = 73,
                        Date6 = 74,
                        AddMonth = 75,
                        BegOfMonth = 76,
                        EndOfMonth = 77,
                        BegOfQuart = 78,
                        EndOfQuart = 79,
                        BegOfYear = 80,
                        EndOfYear = 81,
                        Year = 82,
                        Month = 83,
                        Day = 84,
                        Hour = 85,
                        Minute = 86,
                        Second = 87,
                        DayOfYear = 88,
                        WeekOfYear = 89,
                        WeekDay = 90,
                        BegOfWeek = 91,
                        EndOfWeek = 92,
                        BegOfDay = 93,
                        EndOfDay = 94,
                        BegOfHour = 95,
                        EndOfHour = 96,
                        BegOfMinute = 97,
                        EndOfMinute = 98,
                        CurDate = 99,
                
                        StrReplace = 100,
                        LinesCount = 101,
                        LineGet = 102,
                
                        Min = 103,
                        Max = 104,
                        StrCountOccur = 105,
                        ErrorDescr = 106,
                        TypeOf = 107,
                        Type = 108,
                        Eval = 109,
                        Format = 110,
                        NewObject = 111,
                
                        ACos = 112,
                        ASin = 113,
                        ATan = 114,
                        Cos = 115,
                        Exp = 116,
                        Log = 117,
                        Log10 = 118,
                        Pow = 119,
                        Sin = 120,
                        Sqrt = 121,
                        Tan = 122,
                
                        AddHndlr = 123,
                        AddObjHndlr = 124,
                        RmvHndlr = 125,
                        RmvObjHndlr = 126,
                        Title = 127,
                        ErrorInfo = 128
                
                


                Сравните с CIL: en.wikipedia.org/wiki/List_of_CIL_instructions

                Intellisense в 1С 8.3 есть, подсказки глобальных функций, подсказки реквизитов определенных типов, подсказки по объектам метаданных, так же все функции имеющие стандарт комментировая так же подсказываются с пояснениями что за аргумент и какого он типа при наборе. Надо просто научиться всем этим пользоваться =)

                «Стандарт комментирования» уже упоминал. XmlDoc – это стандарт, а то, что у вас показано в тексте – это бардак. За Intellisens в 8.3 спасибо – долго его ждали. Наверное, Снегопат повлиял на компанию 1С в сторону внимания программистам. В C# достаточно указать тип входных параметров и студия подключит для них Intellisens, что согласитесь намного проще, чем писать простыню комментариев перед функцией. Я упоминал еще рефакторинг. 1С умеет автоматически переименовывать по коду переменные или снова я пользоваться не умею?
                  0
                  Для контроля версий есть Хранилище конфигурации, афигенно удобный инстурмент, в котором можно захватывать и помещать объекты общей конфигурации, чтобы не было коллизий между группой разработчиков.

                  Можете подсказать, как через «афигенно» удобный инструмент сделать ветвления? А если он не позволяет делать ветвления, что было бы удобно для поддержки разных однотипных конфигураций, какой же он «афигенно» удобный? Недавно тема была про муки внесения изменений после очередного обновления от 1С. С ветвлением проблем возникало бы меньше. Хранилище – это пародия на Visual SourceSafe, который давно вымер, не выдержав конкуренции с SVN, GIT.

                  Но можно так же и обычный Git использовать, достаточно выгрузить конфигурацию в промежуточные файды (txt, xml, mxl, epf, ert), но именно для 1С это менее удобно, чем использовать хранилище.


                  Наверное, снова разочарую вас, но практически использовать в GIT выгрузку конфигурации XML у вас вряд ли получится. Попробуйте поработать с миллионом скинутых в один каталог файлов, где не работает функция сравнения для mxl, epf, erf форматов. Почему компании 1С нельзя было их разложить сразу по папкам Объект/Назвние/<Файлы объекта>? Кстати, внешние отчеты/обработки до сих пор не выгружаются в XML.

                  В 8.3 есть unit тестирование, ровно как и нагрузочное тестирование, как и оценка производительности в «особых» условиях.

                  Рад, что хоть в чем-то 1С преуспела. Мало знаком с этими технологиями, поэтому остается только надеяться, что с ними все хорошо, а не как в остальных случаях.
                    +1
                    Наверное, снова разочарую вас, но практически использовать в GIT выгрузку конфигурации XML у вас вряд ли получится. Попробуйте поработать с миллионом скинутых в один каталог файлов, где не работает функция сравнения для mxl, epf, erf форматов. Почему компании 1С нельзя было их разложить сразу по папкам Объект/Назвние/<Файлы объекта>? Кстати, внешние отчеты/обработки до сих пор не выгружаются в XML.


                    1С этого не сделала, но средства к этому все-таки есть.

                    github.com/xDrivenDevelopment/v83unpack — Человечная работа 1С с git. В частности, раскладывание кучи файлов 1С в нормальные папки, там есть.
                    sourceforge.net/projects/v8reader/ — сравнение epf/erf находящихся в репозитории (автор — я, так что немного ПИАРа)
                    infostart.ru/public/106310/ — аналогично, но для всех объектов конфигурации. Цепляете как external diff и вперед.

                    В сообществе вообще много инструментов для правильной разработки на 1С. Те же юнит-тесты, например, у нас во внутренней разработке применяются очень активно и приносят должный эффект.

                    Если 1С чего-то не сделала, то это не значит, что этого нет.
              +1
              Возник вопрос со строкой
              var Model = Compilation.GetSemanticModel(tree);

              она не работает, т.к. GetSemanticModel является нестатическим методом, с конструктор типа Compilation — internal
                +1
                Имелся ввиду объект compilation — с прописной буквой «C» — изменил в статье. Теперь выглядит так:
                var compilation = CSharpCompilation.Create("1ccode").WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)).AddReferences(new MetadataFileReference(typeof(object).Assembly.Location)).AddSyntaxTrees(tree);
                var Model = compilation.GetSemanticModel(tree);
                
                0
                Помимо чисто исследовательского интереса — какая, на ваш взгляд, может быть практическая польза от этого? Если может, конечно.
                  +1
                  Выше написал комментарий
                  Как оказалось, у подхода компилирования C# в код 1С могут быть еще достоинства. Разработка 1С из конфигуратора в теории может быть перемещена в Visual Studio со всеми преимуществами: intellisense, быстрый рефакторинг, контроль типов, встроенный контроль ошибок при компилировнии. Плюс контроль версий сторонними средствами (проект C# в виде файлов в папке). Возможно, юнит-тестирование. Еще код на C# более выразительный. Сложный проект можно разделить на модули-сборки проектов C#.

                  Вероятность того, что будет польза минимальная, потому что для успешного результата требуется: проработать трансляцию конструкций C# на 1C (встроенные типы, события, классы, generic-типы, лямбда выражения), сделать классы/методы-заглушки для всех объектов 1С (их очень много), собрать сообщество, поддерживать в актуальном состоянии с вновь выходящими фичами 1С.

                  Я интересовался вашим opensource-проектом, вы можете тоже проработать такой подход для других языков. Но в случае с VisualStudio/C# и сборками, в которые можно помещать все, что угодно, организовать трансляцию проще и удобнее.
                    0
                    Вы очень верно меня раскусили) Я спрашиваю не просто так, а с прицелом на то, чтобы как-то применить это в своем проекте и, возможно, привлечь к нему вас ;) 1Script, кстати, из эксперимента вырос в прикладной инструмент и уже используется в боевых задачах.
                      0
                      Идею здесь, думаю, можно выразить так: используя современные IDE (Eclipse, Visual Studio) и языки программирования компилировать исходный код в низкоуровневый код 1С (или опкод 1С) и набор XML-файлов для загрузки в конфигуратор. Сделать это нужно, чтобы компании 1С стало стыдно за невыразительность языка :)
                      А можно ли применить к вашему проекту, вам, думаю будет видней.
                  0
                  Как могло бы выглядеть определение Перечисления в коде C#, которое теоретически можно было бы транслировать в аналогичное определение 1С
                      [Uuid("e25d7fed-761e-4247-923e-01b2a1df479b")]
                      [GeneratedType(GeneratedTypeCategory.Ref, "39091fcf-4739-4c06-ad24-085f96fff7f2", "4ebf46c2-ce91-44f8-96d5-ac7dc143095f")]
                      [GeneratedType(GeneratedTypeCategory.Manager, "7fb5c2f6-6579-4734-9dce-4e3db33c0100", "26e51616-0d45-4ee1-b90a-4a9aa2682ec7")]
                      [GeneratedType(GeneratedTypeCategory.List, "32f4ed6d-cd34-4a19-8cf1-ad290cbad5f2", "e2039c40-5bdd-4a07-9a94-4693575d8c08")]
                      public class СтатусыИзвлеченияТекстаФайлов : Enumeration
                      {
                          [Synonym("ru", "Не извлечен")]
                          public static readonly EnumRef<СтатусыИзвлеченияТекстаФайлов> НеИзвлечен = new EnumRef<СтатусыИзвлеченияТекстаФайлов>(new Guid("e9bab7c9-2560-41bb-a4e2-d3c05bc622ef"));
                          [Synonym("ru", "Извлечен")]
                          public static readonly EnumRef<СтатусыИзвлеченияТекстаФайлов> Извлечен = new EnumRef<СтатусыИзвлеченияТекстаФайлов>(new Guid("bf28a953-23ae-4308-891f-c51f47f95e6c"));
                          [Synonym("ru", "Извлечь не удалось")]
                          public static readonly EnumRef<СтатусыИзвлеченияТекстаФайлов> ИзвлечьНеУдалось = new EnumRef<СтатусыИзвлеченияТекстаФайлов>(new Guid("543b7113-448a-4e17-bb7c-8fef149aa8ed"));
                  
                          public override void ChoiceDataGetProcessing(object ChoiceData, object Parameters, ref bool StandardProcessing)
                          {
                              //код для ОбработкаПолученияДанныхВыбора
                          }
                      }
                  

                  Only users with full accounts can post comments. Log in, please.