Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming

Write C# and Java Code using Code

4.88/5 (9 votes)
29 Jun 2010CPOL5 min read 35.1K   365  
The code writer allows to transparently write code for C# and Java, for applications like serialization, equality, etc.

Introduction

This article will introduce a framework that will make it possible to write a piece of code once, and almost transparently generate the same logical code for both C# and Java, with the specific syntax for each language. The framework lets you write code that generates code: it exposes programming constructs such as classes, methods, loops, etc.

On top on this framework, one may use any method to create the code that he or she wants. Reflection will probably be in common use since it allows to automate the code generation according to the structure of existing types.

Background

A common task when writing an interface between C# and Java is to maintain a similar set of objects in both environments, as well as supporting serialization for those objects. There are solutions like web services for this task, but sometimes performance constraints make them impossible to use. Even when we use one technology, the same constraints may force us to write a custom serialization code, rather than using the built in serialization that tend to create large serialized objects.

Another common requirement is overriding basic methods like Equals, GetHashCode, Clone, ToString, etc. Many times, this is summed up in doing almost the exact operation on all object properties, e.g., checking the equality of all properties in Equals. We can use reflection for this, but there may be a performance impact, besides the other problems with reflection.

The framework allows to automatically do all of these tedious tasks using reflection for both C# and Java.

Using the Code

The main Class to use is called CodeWriter. It's an abstract class that receives a stream to write to, and exposes all the operations that allows you to write the code. There are 2 implementations of this class: CsharpCodeWriter and JavaCodeWriter, that do what you might expect.

On top of that, the Expressions namespace contains all sorts of classes that can be used as elements in the different operations. For example:

C#
codeWriter.WriteField(AccessModifier.Private, propertyType, field, 
	new ConstructorExpression(propertyType));	   

The WriteField operation writes a field, and receives a ConstructorExpression as initial value.

We'll now go into details with 2 uses I found for this framework. The first one is quite simple: Equals method:

C#
private static void WriteEquals(CodeWriter codeWriter, Type type)
{
    // Write the method signature
    StringExpression obj = new StringExpression("obj");
    IDictionary<StringExpression, Type> parameters = 
			new Dictionary<StringExpression, Type>();
    parameters.Add(obj, typeof(object));
    codeWriter.StartMethod(AccessModifier.Public, 
	MethodModifier.Override, typeof(bool), "Equals", parameters);

    // Check reference equality
    LogicalBinaryExpression lbe = new LogicalBinaryExpression
		(obj, LogicalBinaryOperator.EQUAL, StringExpression.THIS);
    codeWriter.WriteIfStart(lbe);
    codeWriter.WriteReturn(StringExpression.TRUE);
    codeWriter.EndBlock();
    codeWriter.WriteNewLine();

    // Check for null and type
    LogicalNnaryExpression lne = new LogicalNnaryExpression();
    lne.Add(new LogicalBinaryExpression(obj, LogicalBinaryOperator.EQUAL, 
		StringExpression.NULL), LogicalNnaryOperator.OR);
    lne.Add(new NotExpression(new LogicalBinaryExpression(obj, 
		LogicalBinaryOperator.IS, new StringExpression(type.Name))));
    codeWriter.WriteIfStart(lne);
    codeWriter.WriteReturn(StringExpression.FALSE);
    codeWriter.EndBlock();
    codeWriter.WriteNewLine();

    // Convert the object
    StringExpression realObj = new StringExpression("realObj");
    codeWriter.WriteVariableDeclaration(type, realObj, new CastExpression(type, obj));
            
    // Check each and every field
    foreach (PropertyInfo property in type.GetProperties())
    {
        Type propertyType = property.PropertyType;
        StringExpression propertyName = new StringExpression(property.Name);
        string equalsMethodName = "Equals";

        // C# List equals check reference equality, 
        // while Java check for members equality. 
        // We need to use SequenceEqual extension method in C#
        if ((IsGenericList(propertyType)) && codeWriter is CSharpCodeWriter)
        {
            equalsMethodName = "SequenceEqual";
        }
        NotExpression ne = new NotExpression
		(new CallMethodExpression(new GetPropertyExpression(propertyName), 
                               equalsMethodName,
                               new GetPropertyExpression(realObj, propertyName)));
        codeWriter.WriteIfStart(ne);
        codeWriter.WriteReturn(StringExpression.FALSE);
        codeWriter.EndBlock();
    }
    codeWriter.WriteReturn(StringExpression.TRUE);

    // End method
    codeWriter.EndBlock();
} 

It may seem a little complex at first, but you'll find out that it really isn't. The parameters for this method are the writer, and a type to implement Equals for. Using the writer, I write the usual code I'll write for Equals method: I check if the references are equals, then I check if the given object is not null, and if it's of the correct type, and after that, I just check that each property is equals in both objects. The result:

C#

C#
public override bool Equals(Object obj)
{
	if(obj == this)
	{
		return true;
	}
	
	if(obj == null || !(obj is MyObject))
	{
		return false;
	}
	
	MyObject realObj = (MyObject)obj;
	if(!(MyField.Equals(realObj.MyField)))
	{
		return false;
	}
	if(!(MyObjectField.Equals(realObj.MyObjectField)))
	{
		return false;
	}
	if(!(MyListField.SequenceEqual(realObj.MyListField)))
	{
		return false;
	}
	if(!(MyEnumField.Equals(realObj.MyEnumField)))
	{
		return false;
	}
	if(!(MyDateTimeField.Equals(realObj.MyDateTimeField)))
	{
		return false;
	}
	return true;
}

Java

Java
public boolean equals(Object obj) {
	if(obj == this) {
		return true;
	}
	
	if(obj == null || !(obj instanceof MyObject)) {
		return false;
	}
	
	MyObject realObj = (MyObject)obj;
	if(!(getMyField().equals(realObj.getMyField()))) {
		return false;
	}
	if(!(getMyObjectField().equals(realObj.getMyObjectField()))) {
		return false;
	}
	if(!(getMyListField().equals(realObj.getMyListField()))) {
		return false;
	}
	if(!(getMyEnumField().equals(realObj.getMyEnumField()))) {
		return false;
	}
	if(!(getMyDateTimeField().equals(realObj.getMyDateTimeField()))) {
		return false;
	}
	return true;
} 

Once you have a basic code, you'll discover that it's really easy to write a code that generates it. Consider how much boring and tedious work is avoided once you do it. Also note that this method actually works for each type you'll give it, since it's using reflection to discover the type properties.

Some smalls details: In Java, List's equals method is implemented by calling equals for each member in the list. In C#, this is implemented using default reference equality. I like the Java implementation, and luckily C# has an extension method called SequenceEqual that does just that. This is a good example to cases where something needs to be added on top of the framework to support one of the programming languages. What is already done for you, if you haven't noticed, is conversion of method and type names. The framework knows to convert all method names to Java convention (First lowercase letter). JavaCodeWriter also accepts dictionaries that convert C# types and methods to Java ones. For example, there is a built in conversion between C# GetHashCode and Java hashCode, and between all C# primitive types to Java primitive types (bool=boolean, etc.).

Now for a slightly more complicated example, serialization:

C#
private static void WriteSerialization(CodeWriter codeWriter, Type type)
{
    // Write method signature
    StringExpression obj = new StringExpression("obj");
    StringExpression writer = new StringExpression("writer");
    IDictionary<StringExpression, Type> parameters = 
			new Dictionary<StringExpression, Type>();
    string typeName = type.Name;
    parameters.Add(obj, type);
    parameters.Add(writer, typeof(BinaryWriter));
    codeWriter.StartMethod(AccessModifier.Public, "Serialize", parameters);

    // Write all properties
    foreach (PropertyInfo propertyInfo in type.GetProperties())
    {
        Type propertyType = propertyInfo.PropertyType;
        string propertyName = propertyInfo.Name;
        GetPropertyExpression property = 
	new GetPropertyExpression(obj, new StringExpression(propertyName));

        // Write list
        if (IsGenericList(propertyType))
        {
            ValueExpression size = codeWriter.CreateListSize(property);
            Type enumerableType = propertyType.GetGenericArguments()[0];
            WriteEnumerableSerialization
		(codeWriter, writer, size, enumerableType, property);
        }
        // Write non list
        else
        {
            WritePropertySerialization(property, propertyType, writer, codeWriter);
        }
    }

    // End method
    codeWriter.EndBlock();
}

private static void WriteEnumerableSerialization(CodeWriter codeWriter,
                                                 StringExpression writer,
                                                 ValueExpression size,
                                                 Type enumerableType,
                                                 ValueExpression property)
{
    // Write the list size
    string methodName = GetWriteMethodName(codeWriter, enumerableType);
    codeWriter.WriteExpression(new CallMethodExpression(writer, methodName, size));
            
    // Write each member of the list
    StringExpression listObj = new StringExpression("listObj");
    codeWriter.StartForEachLoop(enumerableType, listObj, property);
    WritePropertySerialization(listObj, enumerableType, writer, codeWriter);
    codeWriter.EndBlock();
}

private static void WritePropertySerialization
    (ValueExpression property, Type type, StringExpression writer, CodeWriter codeWriter)
{
    // Java DataOutputStream doesn't supply a decent method for serializing string, 
    // so we have to do it on our own
    string typeName = type.Name;
    if (type == typeof(string) && codeWriter is JavaCodeWriter)
    {
        // Write the string length
        ValueExpression stringLength = new CallMethodExpression(property, "length");
        WritePropertySerialization(stringLength, typeof(int), writer, codeWriter);

        // Write the string as bytes if it's not empty
        codeWriter.WriteIfStart(new LogicalBinaryExpression
	(stringLength, LogicalBinaryOperator.GREATER_THEN, new StringExpression("0")));
        codeWriter.WriteExpression(new CallMethodExpression
			(writer, "writeBytes", property));
        codeWriter.EndBlock();
    }
    // Simple serialization is serialization we can do 
    // "inline" using the serialization writer methods
    else if (IsSimpleSerialization(type))
    {
        ValueExpression ve;

        // Enum is serialized as int
        if (type.IsEnum)
        {
            ve = new CastExpression(typeof(int), property);
        }
        // Date ticks are serialized as long
        else if (type == typeof(DateTime))
        {
            ve = new GetPropertyExpression(property, new StringExpression
		(codeWriter is CSharpCodeWriter ? "Ticks" : "Time"));
        }
        // Each other simple property is serialized as is
        else
        {
            ve = property;
        }
        // Use the appropriate method to serialize the property
        codeWriter.WriteExpression(new CallMethodExpression
		(writer, GetWriteMethodName(codeWriter, type), ve));
    }
    // Composite property, uses method signature that assumes 
    // we generate the serialization of this type using the generator as well
    else
    {
        codeWriter.WriteExpression(new CallMethodExpression
			("Serialize", property, writer));
    }
} 

There are many details, but the principal stays the same. What I do is to create a method that receives an object that does the serialization, and the object to serialize. In C# case, I chose to use BinaryWriter for serialization, and in Java I use DataOutputStream. I passed the conversion between this 2 types when I created the JavaCodeWriter. After that is done, all I need to do is to serialize each and every property. There are some cases based on the type of the property:

  • List - I serialize the size, and then serialize each member according to the members type
  • String - In C#, I can use the BinaryWriter.Write method. No such luck in Java. I have to serialize the string size, and then to serialize the string itself as byte array
  • Enum - Serialized as integer
  • DateTime - The long Ticks property is serialized. In Java, this class is called Date, and it has a similar property named Time
  • Primitive types - Serialized using the BinaryWriter.Write method. In Java, each type has its own method, e.g. DataOutputStream.writeInt
  • Any other object - I call the method with signature assuming that serialization will be implemented using the same solution

And the result:

C#

C#
public void Serialize(MyObject obj, BinaryWriter writer)
{
	writer.Write(obj.MyField);
	Serialize(obj.MyObjectField, writer);
	writer.Write(obj.MyListField.Count);
	foreach(long listObj in obj.MyListField)
	{
		writer.Write(listObj);
	}
	writer.Write((int)obj.MyEnumField);
	writer.Write(obj.MyDateTimeField.Ticks);
}

Java

Java
public void serialize(MyObject obj, DataOutputStream writer) {
	writer.writeInt(obj.getMyField());
	serialize(obj.getMyObjectField(), writer);
	writer.writeLong(obj.getMyListField().size());
	for(long listObj : obj.getMyListField()) {
		writer.writeLong(listObj);
	}
	writer.writeInt((int)obj.getMyEnumField());
	writer.writeLong(obj.getMyDateTimeField().getTime());
}

So much generation code for so little serialization code. Of course, this is not true to the real objects that we use. One itty bitty comment: the above code doesn't work. It can work, but something has to be done first. The byte order in Java is opposite to the byte order in C#. This means that if you write a short value, which is composed of 2 bytes, you will get the 2 bytes in different order for each technology. The solution we found for this problem is inheriting BinaryWriter, and using IPAddress.HostToNetworkOrder to write numbers.

Conclusion

Besides the above code, you can get implementations for deserialization, GetHashCode, as well as code that writes the C# enums and classes to Java enums and classes. You should be able to easily implement additional methods like ToString and Clone. The framework and the examples are all documented.

From some point during the implementation of this framework, it became very easy to add additional constructs, so if you miss things like while loop or ? : operator you should be able to easily add those as well. In any case, I hope you found this useful.

License

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