honey the codewitch Ответов: 2

Как сделать массаж codedom для генерации этого кода в правильном ВБ?


извините, что я не говорю на VB, так что вот код C#, который я пытаюсь визуализировать с помощью CodeDom

(все это работает персиковый рендеринг на C# только не VB)

var value =  new System.Collections.Generic.KeyValuePair<string, int>[0];
    
var expression = CodeDomUtility.Serialize(value);


(спасибо Ричарду Деммингу в комментариях за то, что он помог мне получить правильный упрек)

Может ли кто-нибудь показать мне дерево codeDom для этого, которое будет производить VB-код, который не содержит синтаксической ошибки при создании?

когда я использую класс на некоторых массивах, которые я сгенерировал, я получаю такие вещи в VB

.. New System.Collections.Generic.KeyValuePair(-1Of String, Integer)() {}


но только для пустых. Массивы с элементами в них работают нормально.

Я даже пробовал специальную оболочку для пустых массивов и объявлял их без инициализаторов в codedom

Что я уже пробовал:

Большая работа комментируется - особый случай для KeyValuePair, но все это взаимосвязано

static partial class CodeDomUtility
{
	static CodeExpression _SerializeArray(Array arr)
	{
		// TODO: Spitting out garbage on empty SZ arrays in VB, with spurious -1's in the code
		// of the array declaration and i have no idea why.
		if (1 == arr.Rank && 0 == arr.GetLowerBound(0))
		{
			var result = new CodeArrayCreateExpression( arr.GetType());
			foreach (var elem in arr)
				result.Initializers.Add(Serialize(elem));
			return result;
		}
		throw new NotSupportedException("Only SZArrays can be serialized to code.");
	}
	public static CodeExpression Serialize(object val)
	{
		if (null == val)
			return new CodePrimitiveExpression(null);
		if (val is bool || 
			val is string || 
			val is short || 
			val is ushort || 
			val is int || 
			val is uint || 
			val is ulong || 
			val is long || 
			val is byte || 
			val is sbyte || 
			val is float || 
			val is double || 
			val is decimal ||
			val is char)
		{
			return new CodePrimitiveExpression(val);
		}
		if (val is Array && 1 == ((Array)val).Rank && 0 == ((Array)val).GetLowerBound(0))
		{
			return _SerializeArray((Array)val);
		}
		var conv = TypeDescriptor.GetConverter(val);
		if (null != conv)
		{
			if (conv.CanConvertTo(typeof(InstanceDescriptor)))
			{
				var desc = conv.ConvertTo(val, typeof(InstanceDescriptor)) as InstanceDescriptor;
				if (!desc.IsComplete)
					throw new NotSupportedException(string.Format("The type \"{0}\" could not be serialized.", val.GetType().FullName));
				var ctor = desc.MemberInfo as ConstructorInfo;
				if (null != ctor)
				{
					var result = new CodeObjectCreateExpression(ctor.DeclaringType);
					foreach (var arg in desc.Arguments)
						result.Parameters.Add(Serialize(arg));
					return result;
				}
				throw new NotSupportedException(string.Format("The instance descriptor for type \"{0}\" is not supported.", val.GetType().FullName));
			}
			else
			{ 
				// we special case for KeyValuePair types.
				// TODO: research a better way to do this
				if (val.GetType().FullName.StartsWith("System.Collections.Generic.KeyValuePair`2"))
				{
					// TODO: Find out what needs to happen to make it work with VB
					var kvpType = new CodeTypeReference(typeof(KeyValuePair<,>));
					foreach (var arg in val.GetType().GetGenericArguments())
						kvpType.TypeArguments.Add(arg);
					var result = new CodeObjectCreateExpression(kvpType);
					for(int ic= kvpType.TypeArguments.Count,i = 0;i<ic;++i)
					{
						var prop = val.GetType().GetProperty(0==i?"Key":"Value");
						result.Parameters.Add(Serialize(prop.GetValue(val)));
					}
					return result;
				}
				throw new NotSupportedException(string.Format("The type \"{0}\" could not be serialized.", val.GetType().FullName));
			}
		}
		else
			throw new NotSupportedException(string.Format("The type \"{0}\" could not be serialized.", val.GetType().FullName));
	}
}

Richard Deeming

Ваш код, кажется, работает для меня:
Демонстрация[^]

Тот самый VB.NET выход есть:

New System.Collections.Generic.KeyValuePair(Of Integer, String)() {New System.Collections.Generic.KeyValuePair(Of Integer, String)(-21, "ab")}


NB: То "исследуйте лучший способ сделать это" это похоже на ваш вопрос на днях:
Type valType = val.GetType();
if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))

honey the codewitch

- Упрекнул я Ричарда. Проверьте свою демо - ссылку

Richard Deeming

Моя демонстрационная ссылка все еще показывает тот же код и результаты. Вы нажали кнопку "Сохранить" и получили новую ссылку?

honey the codewitch

возгласы. извините, никогда раньше не пользовался этим сайтом. я думал, что он просто отредактирован на месте.

вот ты где: Демонстрация

Richard Deeming

Ладно, похоже, это как-то связано с пустым массивом. Изменение значения на:

var value =  new System.Collections.Generic.KeyValuePair<string, int>[0];

производит проблематичное VB.NET код:
New System.Collections.Generic.KeyValuePair(-1Of String, Integer)() {}

Обновленная демо-версия[^]

honey the codewitch

я уточню свой вопрос. Спасибо. Я попытался не использовать инициализаторы, а просто установить его в [0] в CodeDom, но это ничего не изменило. Возможно, мне придется использовать специальный случай для VB и вставить объект CodeSnippet, чтобы сделать это =(

спасибо за вашу помощь. Я только что выпил кофе и сегодня утром был медлителен.

Richard Deeming

Определенно выглядит как жучок в VBCodeProvider. Он не может справиться с массивами любого универсального типа.
Опорный источник[^]

honey the codewitch

у вас есть какой-то серьезный google fu. Наверное, я заржавел. Честно говоря, я хотел объявить эту таблицу DFA простым вложенным массивом int, как мои таблицы синтаксического анализа, но, несмотря на то, что DFA (структура, которую я сериализую выше) концептуально проще, чем PDA (ints, которые я сериализую) математически, структуры данных для них более сложны, чем позволяет такое решение вложенного массива.

Я подумываю о том, чтобы объявить DfaEntry структура и покончим с этим.

Честно говоря я даже не знаю правильного синтаксиса для этого в VB так что я не могу действительно винить VBCodeProvider LOL

honey the codewitch

Спасибо, Ричард. Я думаю, что не сделал РЕПО правильно, потому что тот же код ломается на моей машине.

Я бы дал вам фактические данные, которые я сериализую в codedom, но это страницы длиной.

2 Ответов

Рейтинг:
2

Member 9772125

Private Sub SurroundingSub()
    Dim kvp = KeyValuePair(Of Integer, String)(_)

    If True Then
        New KeyValuePair(Of Integer, String)(-21, New String From {
            "ab"
        })
    End If
End Sub


honey the codewitch

я имею в виду, конечно. но теперь как заставить codedom сделать это правильно.

Рейтинг:
18

Richard Deeming

Похоже VBCodeProvider не могу справиться с пустыми массивами универсальных типов:
referencesource/VBCodeProvider.cs at master · microsoft/referencesource · GitHub[^]

Если тип элемента массива является универсальным типом, то значение, возвращаемое из GetTypeOutput уже будет содержать скобки - например: KeyValuePair(Of String, Integer).

Затем код неправильно вставляет верхнюю границу массива (-1) внутри первой скобки, нарушая сгенерированный код - например: KeyValuePair(-1Of String, Integer).

Наверное, так и должно быть сообщите об этом в Microsoft как об ошибке[^Но я бы не стал задерживать дыхание в ожидании дозы! :)


honey the codewitch

каков правильный синтаксис для объявления этого (конечно, ограниченного нулем) в VB?

если ты знаешь.

Richard Deeming

Что-то вроде этого должно сработать:

New KeyValuePair(Of String, Integer)() { }

honey the codewitch

Я думаю, что это один ограниченный, как и дефолт VB. Я должен проверить.

Richard Deeming

На моей машине он возвращает массив с нулевой длиной.

honey the codewitch

На днях я должен установить VB =D

Richard Deeming

Неееет! Не делай этого! :Д

(Вы всегда можете использовать Помощью linqpad[^] для тестирования фрагментов VB.)