Click here to Skip to main content
15,867,308 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
sorry I don't speak VB, so here's the C# code I am trying to render with the CodeDom

(this all works peachy rendering to C# just not VB)

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


(thanks to Richard Demming in the comments for helping me get a proper repro)

Can someone show me a codeDom tree for this that will produce VB code which doesn't syntax error when generated?

when i use the class on some arrays i've generated i get stuff like this in the VB

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


but only for the empty ones. The arrays with items in them work fine.

I've even tried special casing for empty arrays and declaring them without initializers in the codedom

What I have tried:

The big work is commented - the special case for KeyValuePair but it's all interrelated

C#
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));
	}
}
Posted
Updated 25-Jul-19 4:07am
v3
Comments
Richard Deeming 25-Jul-19 9:13am    
Your code seems to work for me:
Demo[^]

The VB.NET output is:
New System.Collections.Generic.KeyValuePair(Of Integer, String)() {New System.Collections.Generic.KeyValuePair(Of Integer, String)(-21, "ab")}


NB: The "research a better way to do this" is similar to your question from the other day:
Type valType = val.GetType();
if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
honey the codewitch 25-Jul-19 9:23am    
Richard I repro'd it. Check your Demo link
Richard Deeming 25-Jul-19 9:24am    
My demo link is still showing the same code and results. Did you click the "Save" button and get a new link?
honey the codewitch 25-Jul-19 9:26am    
whoops. sorry, never used the site before. i thought it just edited in place.

here you go: Demo
Richard Deeming 25-Jul-19 9:37am    
OK, looks like it's something to do with the empty array. Changing the value to:
var value =  new System.Collections.Generic.KeyValuePair<string, int>[0];

produces the problematic VB.NET code:
New System.Collections.Generic.KeyValuePair(-1Of String, Integer)() {}

Updated demo[^]

Looks like the VBCodeProvider can't cope with empty arrays of generic types:
referencesource/VBCodeProvider.cs at master · microsoft/referencesource · GitHub[^]

When the array element type is a generic type, the value returned from GetTypeOutput will already contain parentheses - eg: KeyValuePair(Of String, Integer).

The code then incorrectly inserts the upper-bound of the array (-1) inside the first parenthesis, breaking the generated code - eg: KeyValuePair(-1Of String, Integer).

You should probably report this to Microsoft as a bug[^]. But I wouldn't hold my breath waiting for a fix! :)
 
Share this answer
 
Comments
honey the codewitch 25-Jul-19 10:09am    
what's the proper syntax for declaring this (zero bounded of course) in VB?

if you know.
Richard Deeming 25-Jul-19 10:19am    
Something like this should work:
New KeyValuePair(Of String, Integer)() { }
honey the codewitch 25-Jul-19 10:20am    
I think that's one bounded, like VB's default. I'll have to check.
Richard Deeming 25-Jul-19 10:24am    
On my machine, it returns an array with zero length.
honey the codewitch 25-Jul-19 10:25am    
One of these days, I should install VB =D
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
 
Share this answer
 
Comments
honey the codewitch 24-Jul-19 15:19pm    
i mean sure. but now how to get the codedom to render that properly.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900