Compiling methods for optimal runtime performance - c#

I'm trying to determine what the best way to get fully optimized delegates for various objects to improve the speed of serialization. Simply put: I'd like to remove various different checks, and compile more efficient serialize functions one time at the start of my app.
Let's take a look at this simple example:
public class GamePacket
{
[Length(10)]
[ReadBackwards]
public string Id { get; set; }
}
Now, I'd likely create a serializer, and for performance reasons store the attributes in a cached field. Everytime I want to deserialize a GamePacket from a stream (or byte array), I'd call something like:
Deserialize(byte[] stream)
{
var header = stream.ReadByte();
var packet = cachedDeserializers[header];
var instance = packet.DelegateForCreateInstance();
foreach (var field in packet.Fields)
{
if (field.Type != TypeCode.String) continue;
var str = stream.ReadBytes(field.LengthAttribute.Length);
if (field.HasReadBackwardsAttribute)
str = str.Reverse();
field.DelegateForSetValue(instance, str);
}
}
The problem now lies in the fact that EVERY time I'm calling Deserialize on that stream, I need to loop through and check various things like attributes, and other checks. In the example, these things can potentially be omitted (And maybe more):
if (field.Type != TypeCode.String) continue;
if (field.HasReadBackwardsAttribute)
If I know the field has a read backwards attribute, I'd like to compile a simplified delegate on app start that omits these checks, and simply reads it backwards. Is it possible to create a delegate that can remove unneeded logic? For example:
Deserialize(byte[] stream)
{
var header = stream.ReadByte();
var packet = cachedDeserializers[header];
var instance = packet.CallCachedCompile(stream);
}
// CallCachedCompile for GamePacket would look something like this:
CallCachedCompile(byte[] stream)
{
var instance = this.DelegateForCreateInstance();
var str = stream.ReadBytes(10);
str = str.Reverse();
this.DelegateForSetValue(instance, "Id", str);
return instance;
}
I've looked briefly into expression trees. Would something like this be doable in expression Trees? What would be the most efficient way?

Yes, using code generation approach you can generate delegates for the particular type. So instead of this generic reflection-like code:
foreach (var field in packet.Fields)
{
if (field.Type != TypeCode.String) continue;
var str = stream.ReadBytes(field.LengthAttribute.Length);
if (field.HasReadBackwardsAttribute)
str = str.Reverse();
field.DelegateForSetValue(instance, str);
}
You can generate code for a specific type:
gamePacketInstance.Id = SomeConvertionToString(stream.ReadBytes(field.LengthAttribute.Length).Revers());
The code generation topic is quite big and I don't know what exactly you do inside your delegates. You can generate specific delegates in runtime (emit il or expression trees) or in compile time (source generators). I suggest you to read my article Dotnet code generation overview by example. It will give good overview with examples.

Without using any code generation you can still do this pretty efficiently.
You basically need to use generics and polymorphism to cache all code that you want done for each type that you encounter.
Bearing in mind that properties have underlying methods, we can create delegates to set properties without using code generation.
abstract class DeserializerBase
{
object DeserializePacket(Stream stream);
}
class Deserializer<T> : DeserializerBase where T : new()
{
FieldAction<T>[] fieldActions =
typeof(T).GetProperties()
.Where(p => p.Type == TypeCode.String)
.Select(p => IsReverseAttribute(p)
? new FieldActionReverse<T>
{
Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
Length = GetLengthAttribute(p),
}
: new FieldAction<T>
{
Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
Length = GetLengthAttribute(p),
})
.ToArray();
object DeserializePacket(Stream stream);
{
var packet = new T();
foreach (var action in fieldActions)
action.Deserialize(packet);
}
}
class FieldAction<T>
{
public Action<T, string> Setter;
public int Length;
void Deserialize(Stream stream, T instance)
{
var str = ReadString(stream);
Setter(instance, str);
}
virtual string GetString(Stream stream)
{
return stream.ReadBytes(Length);
}
}
class FieldActionReverse<T> : FieldAction<T>
{
override string GetString(Stream stream)
{
return stream.ReadBytes(Length).Reverse();
}
}
Your final entry code becomes this.
Dictionary<int, DeserializerBase> cachedDeserializers = new Dictionary<int, DeserializerBase>
{
{5, new Deserializer<GamePacket>()}
};
Deserialize(Stream stream)
{
var header = stream.ReadByte();
var packet = cachedDeserializers[header].DeserializePacket(stream);
}
You can even place generic constraints on T to ensure it is a Packet then you can return a base Packet type from this entry function.

Related

Pass Anonymous Type

I am trying to create a utility class where I could pass a list of Anonymous Type (AT) and it would produce a CSV file with the AT's properties as its columns and property values as its respective data.
I have a working code but I feel it could be improved (a lot!). I inherited a class from FileResult and decorate it with my custom implementations. Here's what I have so far:
public class ExportCSVAnonymous : FileResult {
public dynamic List {
set;
get;
}
public char Separator {
set;
get;
}
public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
List = list;
Separator = separator;
FileDownloadName = fileDownloadName;
}
public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
List = list;
Separator = separator;
FileDownloadName = fileDownloadName;
}
protected override void WriteFile(HttpResponseBase response) {
var outputStream = response.OutputStream;
using (var memoryStream = new MemoryStream()) {
WriteList(memoryStream);
outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
}
private void WriteList(Stream stream) {
var streamWriter = new StreamWriter(stream, Encoding.Default);
WriteHeaderLine(streamWriter);
streamWriter.WriteLine();
WriteDataLines(streamWriter);
streamWriter.Flush();
}
//I wish this part could be improved
private void WriteHeaderLine(StreamWriter streamWriter) {
foreach (var line in List) {
foreach (MemberInfo member in line.GetType().GetProperties()) {
WriteValue(streamWriter, member.Name);
}
break;
}
}
private void WriteValue(StreamWriter writer, String value) {
writer.Write("\"");
writer.Write(value.Replace("\"", "\"\""));
writer.Write("\"" + Separator);
}
private void WriteDataLines(StreamWriter streamWriter) {
foreach (var line in List) {
foreach (MemberInfo member in line.GetType().GetProperties()) {
WriteValue(streamWriter, GetPropertyValue(line, member.Name));
}
streamWriter.WriteLine();
}
}
private static string GetPropertyValue(object src, string propName) {
object obj = src.GetType().GetProperty(propName).GetValue(src, null);
return (obj != null) ? obj.ToString() : "";
}
}
I used dynamic as a way to pass my AT inside the class. Is there better way to do this? Lastly, I want to improve the WriteHeaderLine method. Since I am using dynamic type, I cannot cast it successfully to inspect the properties of the AT. What's the best way to do this?
To some extent I feel its a bit overkill, for purposes of writing a CSV, to use an anonymous type (or any type. really) to pass the info purely so you can pass headers too as named properties. I get it, but ask yourself what is the code really doing/what problem are you solving?
You want to write N strings to a file.
That's pretty much it. So you write some simple method:
void WriteCsvLine(path, string[] cells){
File.AppendAllText(path, string.Join(",", cells) + Environment.NewLine);
}
And you call it like:
someContext.Employees.Select(e =>
new [] { e.Name, e.Dept, e.Salary.ToString() }
).ToList().ForEach(x => WriteCsvLine("c:\\...", x) ;
Ah, but we don't want to pass the path every time.. So you upgrade it to be a class, take the path as a constructor arg..
Ah, but we need to escape commas.. So you upgrade it to quote the fields
Ah, but we need to provide some variable delimiter.. So you upgrade it to have another constructor arg
Ah, but we could optimize to write multiple lines at a time.. So you upgrade it to take a List<string[]> or whatever
Ah, but we need to write a header line.. So you just make the first string array you pass to be the headers instead (you can LINQ Concat your data onto a new[]{"Name","Dept","Salary"} or make it a constructor argument..)
So we've need up with something that we maybe use like this:
var x = new CsvWriter("c:\\...", ',', new[]{"EmployeeName","DepartmentName","Salary"});
x.WriteEnumerable(someContext.Employees.Select(e => new [] {
e.Name,
e.Dept,
e.Salary.ToString()
}));
But that's not very cool - surely we can do it cooler.. So you decide you'll pass a KeyValuePair<string, string>[] (or a record or a ValueTuple) where the key is the header and the value is the data.. your calling code bulks up because you're specifying the header name with the data every time..
Ah, but that's still not very cool with all those strings.. So you decide you'll pass an anonymous type where the property name is the header, and the property value is the data..
Your code has some fewer " chars but now the receiving end has become a torturous nightmare of unpacking the property names into being strings so they can be written as a header line.. (and I don't even know if you can easily control the order of columns any more)
Ends up, the problem was simple: "find a way to pass what the header of the column should be", or in other words "pass a string to a method"
..and somehow we went from:
void Print(string what){
Console.WriteLine(what);
}
...
Print("Hello World");
to something like:
using System.Reflection;
static void Print<T>(T what)
{
PropertyInfo[] propertyInfos = what.GetType().GetProperties();
Console.WriteLine(propertyInfos[0].Name.Replace("_", " "));
}
...
Print(new { Hello_World = 0 });
It'll work, but it's a fairly fairly bonkers way of "passing a string to a method" when you think about it..
..and now the boss wants the headers to have percent symbols so I'm off to work out how to get those into property names and also add another bool flag so we can skip writing the header sometimes .. 😀

Roslyn get IdentifierName in ObjectCreationExpressionSyntax

Currently I am working on simple code analyse for c# with roslyn. I need to parse all document of all projects inside one solution and getting the declared used classes inside this document.
For example from:
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
}
}
I want to get Program uses Foo.
I already parse all documents and get the declared class inside.
// all projects in solution
foreach (var project in _solution.Projects)
{
// all documents inside project
foreach (var document in project.Documents)
{
var syntaxRoot = await document.GetSyntaxRootAsync();
var model = await document.GetSemanticModelAsync();
var classes = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>();
// all classes inside document
foreach (var classDeclarationSyntax in classes)
{
var symbol = model.GetDeclaredSymbol(classDeclarationSyntax);
var objectCreationExpressionSyntaxs = classDeclarationSyntax.DescendantNodes().OfType<ObjectCreationExpressionSyntax>();
// all object creations inside document
foreach (var objectCreationExpressionSyntax in objectCreationExpressionSyntaxs)
{
// TODO: Get the identifier value
}
}
}
}
The problem is to get the IdentifierName Foo. Using the debugger, I see objectCreationExpressionSyntax.Typegot the Identifier.Text got the value I need, but objectCreationExpressionSyntax.Type.Identifierseems to be private.
I could use the SymbolFinder to find all references of a Class in the solution. As I already need to parse all documents its should work without.
Maybe I am on the wrong path? How to get the identifier value?
You'll need to handle the different types of TypeSyntaxes. See here: http://sourceroslyn.io/#Microsoft.CodeAnalysis.CSharp/Syntax/TypeSyntax.cs,29171ac4ad60a546,references
What you see in the debugger is a SimpleNameSyntax, which does have a public Identifier property.
Update
var ns = objectCreationExpressionSyntax.Type as NameSyntax;
if (ns != null)
{
return ns.Identifier.ToString();
}
var pts = objectCreationExpressionSyntax.Type as PredefinedTypeSyntax;
if (pts != null)
{
return pts.Keyword.ToString();
}
...
All other subtypes would need to be handed. Note that ArrayType.ElementType is also a TypeSyntax, so you would most probably need to make this method recursive.
You can get the identifier from the syntax's Type property:
foreach (var objectCreationExpressionSyntax in objectCreationExpressionSyntaxs)
{
IdentifierNameSyntax ins = (IdentifierNameSyntax)objectCreationExpressionSyntax.Type;
var id = ins.Identifier;
Console.WriteLine(id.ValueText);
}
Strings can be misleading.
Let's say you have the expression new SomeClass(), and you get the string "SomeClass" out of it. How do you know if that refers to Namespace1.SomeClass or Namespace2.SomeClass ? What if there is a using SomeClass = Namespace3.SomeOtherType; declaration being used?
Fortunately, you don't have to do this analysis yourself. The compiler can bind the ObjectCreationExpressionSyntax to a symbol. You have your semantic model, use it.
foreach (var oce in objectCreationExpressionSyntaxs)
{
ITypeSymbol typeSymbol = model.GetTypeInfo(oce).Type;
// ...
}
You can compare this symbol with the symbols you get from model.GetDeclaredSymbol(classDeclarationSyntax), just make sure you use the Equals method, not the == operator.

Persist an object that is not marked as serializable

I need to persist an object that is not marked with the serializable attribute. The object is from a 3rd party library which I cannot change.
I need to store it in a persist place, like for example the file system, so the optimal solution would be to serialize the object to a file, but since it isn't marked as serializable, that is not a straight forward solution.
It's a pretty complex object, which also holds a collection of other objects.
Do you guys have any input on how to solve this? The code will never run in a production environment, so I'm ok with almost any solution and performance.
XmlSerializer may be a useful first thing to try, if the types are public etc
If that fails, v2 of protobuf-net (in progress, you'd need to build from source, but I can help) works with unattributed objects, so ideal for types outside your control - you just need to tell it what to include (via a DSL). The v2 code isn't complete, but it covers most common scenarios, including collections etc (the incomplete work is mainly callbacks and enums).
You could write a recursive method that would run down the object graph using reflection to persist the object... Putting it back could be much more difficult. Who knows if any of those objects are holding references to unmanaged or system resources. If i was to do anything this nuts, I would go for the .GetFields(...) method on the type.
Another idea...
If you are only doing this to speed up development why not wrap their clases with your own adapter classes. This would allow you to replace the third party libraries with your own simplifed mock classes and allow for better chance for replacement and reuse later.
Sick as it is... This was easier then I thought it would be. (While this works... please consider wrapping the third party classes.)
public static class Tools
{
public static XElement AsXml(this object input)
{
return input.AsXml(string.Empty);
}
public static XElement AsXml(this object input, string name)
{
if (string.IsNullOrEmpty(name))
name = input.GetType().Name;
var xname = XmlConvert.EncodeName(name);
if (input == null)
return new XElement(xname);
if (input is string || input is int || input is float /* others */)
return new XElement(xname, input);
var type = input.GetType();
var fields = type.GetFields(BindingFlags.Instance |
BindingFlags.NonPublic)
.Union(type.GetFields(BindingFlags.Instance |
BindingFlags.Public));
var elems = fields.Select(f => f.GetValue(input)
.AsXml(f.Name));
return new XElement(xname, elems);
}
public static void ToObject(this XElement input, object result)
{
if (input == null || result == null)
throw new ArgumentNullException();
var type = result.GetType();
var fields = type.GetFields(BindingFlags.Instance |
BindingFlags.NonPublic)
.Union(type.GetFields(BindingFlags.Instance |
BindingFlags.Public));
var values = from elm in input.Elements()
let name = XmlConvert.DecodeName(elm.Name.LocalName)
join field in fields on name equals field.Name
let backType = field.FieldType
let val = elm.Value
let parsed = backType.AsValue(val, elm)
select new
{
field,
parsed
};
foreach (var item in values)
item.field.SetValue(result, item.parsed);
}
public static object AsValue(this Type backType,
string val,
XElement elm)
{
if (backType == typeof(string))
return (object)val;
if (backType == typeof(int))
return (object)int.Parse(val);
if (backType == typeof(float))
return (float)int.Parse(val);
object ret = FormatterServices.GetUninitializedObject(backType);
elm.ToObject(ret);
return ret;
}
}
public class Program
{
public static void Main(string[] args)
{
var obj = new { Matt = "hi", Other = new { ID = 1 } };
var other = new { Matt = "zzz", Other = new { ID = 5 } };
var ret = obj.AsXml();
ret.ToObject(other);
Console.WriteLine(obj); //{ Matt = hi, Other = { ID = 1 } }
Console.WriteLine(other); //{ Matt = hi, Other = { ID = 1 } }
}
}
This is one way you could do it:
http://www.codeproject.com/KB/dotnet/Surrogate_Serialization.aspx
here is the msdn link showing it:
http://msdn.microsoft.com/en-us/magazine/cc188950.aspx
I don't know if it is overkill for your usage, but I have been playing around with db4o lately. It will persist any object, just call IObjectContainer.Store(object), and it is lightweight and file-based. Does not require any installation.
I haven't had any problems with it yet.
///Here OBJECT is Class name and Object_to_write is instance
XmlSerializer serializer = new XmlSerializer(typeof(OBJECT));
using (TextWriter writer = new StreamWriter(#"C:\Xml.xml"))
{
serializer.Serialize(writer, OBJECT_to_Write);
}

How to enumerate passed method parameters

One can enumerate the called method parameter types/information like this:
private void SomeMethod(int thisValue, string thatValue)
{
StackTrace stackTrace = new StackTrace();
foreach (ParameterInfo pInfo in stackTrace.GetFrame(0).GetMethod().GetParameters())
{
string name = pInfo.Name;
string type = pInfo.GetType().ToString();
}
}
But is there any way to get the actual object of each parameter?
EDIT:
My goal is to enumerate all parameters and get their values.
Using LinQ Expressions, one can get the parameter value like so:
private void SomeMethod(int thisValue, string thatValue)
{
object valueOfThis = GetParameterValue(() => thisValue);
object valueOfThat = GetParameterValue(() => thatValue);
}
private object GetParameterValue<T>(Expression<Func<T>> expr)
{
var body = ((MemberExpression)expr.Body);
return ((FieldInfo)body.Member).GetValue(((ConstantExpression)body.Expression).Value);
}
But what I would like to do is something like:
foreach (fooObject o in thisMethod.GetParameterObjects())
{
object someValue = GetParameterValue(() => fooObject);
}
And thereby have a generic method for collection all parameters and their values.
UPDATE:
Looks like I "overcomplicated" the initial answer by trying to explain everything. Here is the short version of the answer.
private static void SomeMethod(int thisValue, string thatValue)
{
IEnumerable<object> parameters = GetParameters(() => SomeMethod(thisValue, thatValue));
foreach (var p in parameters)
Console.WriteLine(p);
}
private static IEnumerable<object> GetParameters(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (MemberExpression a in body.Arguments)
{
var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value);
yield return test;
}
}
And here is the long version with some explanations.
In fact, if you use expression trees, you don't need to be inside a method to enumerate its parameters.
static void Main(string[] args)
{
// First approach.
IEnumerable<object> parameters = GetParametersFromConstants(() => SomeMethod(0, "zero"));
foreach (var p in parameters)
Console.WriteLine(p);
// Second approach.
int thisValue = 0;
string thatValue = "zero";
IEnumerable<object> parameters2 = GetParametersFromVariables(() => SomeMethod(thisValue, thatValue));
foreach (var p in parameters2)
Console.WriteLine(p);
Console.ReadLine();
}
private static void SomeMethod(int thisValue, string thatValue)
{
Console.WriteLine(thisValue + " " + thatValue);
}
private static IEnumerable<object> GetParametersFromVariables(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (MemberExpression a in body.Arguments)
{
var test = ((FieldInfo)a.Member).GetValue(((ConstantExpression)a.Expression).Value);
yield return test;
}
}
private static IEnumerable<object> GetParametersFromConstants(Expression<Action> expr)
{
var body = (MethodCallExpression)expr.Body;
foreach (ConstantExpression a in body.Arguments)
{
var test = a.Value;
yield return test;
}
}
}
Note, that if you use expression trees, your code depends a lot on an expression passed to the method. I have shown one using constants and one using variables. But of course there can be more scenarios. You can refactor this code to use a single method for both cases, but I decided that is illustrates the problem better this way.
Okay, so here's the deal.
You can not do that, not from a managed language. I don't see how anyone would allow you to take control of the stack frame. And in a way that's what you want. Because you need the information to get the values.
Now the run-time knows this, it has all the information, but you can not make assumptions on how it will go about creating a stack frame, because you are not meant to do this.
Ergo, there's only one way to go about this. The profiling API.
I end up here. Within the functions of the profiling API. I bet there's a way to do this that let's you dig into the parameter values by invoking a unmanaged class from managed code.
Now, I wouldn't do this because there's great profiling tools out there already, JetBrains dotTrace to name one and with IntelliTrace in VS2010 all these headaches will simply go away... IntelliTrace will let you do time traveling debugging.
The other and obvious way to do this is totally foobar, but might end up fun to experiment with, it can always be done this way, but I would never in my life put this code in a production environment.
// compile with unsafe
unsafe
{
var p = stackalloc int[1];
var baseAddr = p - sizeof(int);
}
Now, you can not write to baseAddr but you should be allowed to read it. The tricky part is to make sense of the stack frames and that has to with the calling convention and that you must know for certain a head of time. Here's a run down of that stuff and it's fastcall.
With this information and the ParameterInfo objects you should be able to walk your way through the arguments.
Since you'll be working with raw pointers you'll need to make those into managed objects, and there's a class for that.
There you go, go nuts!
A big warning though, what you'll find as you walk up the stack, won't be what you expect. Because arguments can be placed in registers and registers can not be accessed from within managed code.
You can use MethodInfo.GetCurrentMethod().GetParameters() to get a list of method parameters. But it's impossible to get their values by reflection.

What is the best way to dump entire objects to a log in C#?

So for viewing a current object's state at runtime, I really like what the Visual Studio Immediate window gives me. Just doing a simple
? objectname
Will give me a nicely formatted 'dump' of the object.
Is there an easy way to do this in code, so I can do something similar when logging?
For a larger object graph, I second the use of Json but with a slightly different strategy. First I have a static class that is easy to call and with a static method that wraps the Json conversion (note: could make this an extension method).
using Newtonsoft.Json;
public static class F
{
public static string Dump(object obj)
{
return JsonConvert.SerializeObject(obj);
}
}
Then in your Immediate Window,
var lookHere = F.Dump(myobj);
lookHere will auto-show up in the Locals window prepended with a $ or you can add a watch to it. On the right hand side of the Value column in the inspector, there is a magnifying glass with a dropdown caret beside it. Choose the dropdown caret and choose Json visualizer.
I am using Visual Studio 2013.
You could base something on the ObjectDumper code that ships with the Linq samples.
Have also a look at the answer of this related question to get a sample.
You could use Visual Studio Immediate Window
Just paste this (change actual to your object name obviously):
Newtonsoft.Json.JsonConvert.SerializeObject(actual);
It should print object in JSON
You should be able to copy it over textmechanic text tool or notepad++ and replace escaped quotes (\") with " and newlines (\r\n) with empty space, then remove double quotes (") from beginning and end and paste it to jsbeautifier to make it more readable.
UPDATE to OP's comment
public static class Dumper
{
public static void Dump(this object obj)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(obj)); // your logger
}
}
this should allow you to dump any object.
Hope this saves you some time.
I'm certain there are better ways of doing this, but I have in the past used a method something like the following to serialize an object into a string that I can log:
private string ObjectToXml(object output)
{
string objectAsXmlString;
System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(output.GetType());
using (System.IO.StringWriter sw = new System.IO.StringWriter())
{
try
{
xs.Serialize(sw, output);
objectAsXmlString = sw.ToString();
}
catch (Exception ex)
{
objectAsXmlString = ex.ToString();
}
}
return objectAsXmlString;
}
You'll see that the method might also return the exception rather than the serialized object, so you'll want to ensure that the objects you want to log are serializable.
ServiceStack.Text has a T.Dump() extension method that does exactly this, recursively dumps all properties of any type in a nice readable format.
Example usage:
var model = new TestModel();
Console.WriteLine(model.Dump());
and output:
{
Int: 1,
String: One,
DateTime: 2010-04-11,
Guid: c050437f6fcd46be9b2d0806a0860b3e,
EmptyIntList: [],
IntList:
[
1,
2,
3
],
StringList:
[
one,
two,
three
],
StringIntMap:
{
a: 1,
b: 2,
c: 3
}
}
Here is a stupidly simple way to write a flat object, nicely formatted:
using Newtonsoft.Json.Linq;
Debug.WriteLine("The object is " + JObject.FromObject(theObjectToDump).ToString());
What's going on is that the object is first converted to a JSON internal representation by JObject.FromObject, and then converted to JSON string by ToString. (And of course a JSON string is a very nice representation of a simple object, especially since ToString will include newlines and indents.) The "ToString" is of course extraneous (as it's implied by using + to concat a string and an object), but I kinda like to specify it here.
You could use reflection and loop through all the object properties, then get their values and save them to the log. The formatting is really trivial (you could use \t to indent an objects properties and its values):
MyObject
Property1 = value
Property2 = value2
OtherObject
OtherProperty = value ...
What I like doing is overriding ToString() so that I get more useful output beyond the type name. This is handy in the debugger, you can see the information you want about an object without needing to expand it.
Following is another version that does the same thing (and handle nested properties), which I think is simpler (no dependencies on external libraries and can be modified easily to do things other than logging):
public class ObjectDumper
{
public static string Dump(object obj)
{
return new ObjectDumper().DumpObject(obj);
}
StringBuilder _dumpBuilder = new StringBuilder();
string DumpObject(object obj)
{
DumpObject(obj, 0);
return _dumpBuilder.ToString();
}
void DumpObject(object obj, int nestingLevel = 0)
{
var nestingSpaces = "".PadLeft(nestingLevel * 4);
if (obj == null)
{
_dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
}
else if (obj is string || obj.GetType().IsPrimitive)
{
_dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
}
else if (ImplementsDictionary(obj.GetType()))
{
using (var e = ((dynamic)obj).GetEnumerator())
{
var enumerator = (IEnumerator)e;
while (enumerator.MoveNext())
{
dynamic p = enumerator.Current;
var key = p.Key;
var value = p.Value;
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
else if (obj is IEnumerable)
{
foreach (dynamic p in obj as IEnumerable)
{
DumpObject(p, nestingLevel);
}
}
else
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
{
string name = descriptor.Name;
object value = descriptor.GetValue(obj);
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
bool ImplementsDictionary(Type t)
{
return t.GetInterfaces().Any(i => i.Name.Contains("IDictionary"));
}
}
I found a library called ObjectPrinter which allows to easily dump objects and collections to strings (and more). It does exactly what I needed.
You can write your own WriteLine method-
public static void WriteLine<T>(T obj)
{
var t = typeof(T);
var props = t.GetProperties();
StringBuilder sb = new StringBuilder();
foreach (var item in props)
{
sb.Append($"{item.Name}:{item.GetValue(obj,null)}; ");
}
sb.AppendLine();
Console.WriteLine(sb.ToString());
}
Use it like-
WriteLine(myObject);
To write a collection we can use-
var ifaces = t.GetInterfaces();
if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
{
dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
while (lst.MoveNext())
{
WriteLine(lst.Current);
}
}
The method may look like-
public static void WriteLine<T>(T obj)
{
var t = typeof(T);
var ifaces = t.GetInterfaces();
if (ifaces.Any(o => o.Name.StartsWith("ICollection")))
{
dynamic lst = t.GetMethod("GetEnumerator").Invoke(obj, null);
while (lst.MoveNext())
{
WriteLine(lst.Current);
}
}
else if (t.GetProperties().Any())
{
var props = t.GetProperties();
StringBuilder sb = new StringBuilder();
foreach (var item in props)
{
sb.Append($"{item.Name}:{item.GetValue(obj, null)}; ");
}
sb.AppendLine();
Console.WriteLine(sb.ToString());
}
}
Using if, else if and checking interfaces, attributes, base type, etc. and recursion (as this is a recursive method) in this way we may achieve an object dumper, but it is tedious for sure. Using the object dumper from Microsoft's LINQ Sample would save your time.
All of the paths above assume that your objects are serializable to XML or JSON,
or you must implement your own solution.
But in the end you still get to the point where you have to solve problems like
recursion in objects
non-serializable objects
exceptions
...
Plus log you want more information:
when the event happened
callstack
which threead
what was in the web session
which ip address
url
...
There is the best solution that solves all of this and much more.
Use this Nuget package: Desharp.
For all types of applications - both web and desktop applications.
See it's Desharp Github documentation. It has many configuration options.
Just call anywhere:
Desharp.Debug.Log(anyException);
Desharp.Debug.Log(anyCustomValueObject);
Desharp.Debug.Log(anyNonserializableObject);
Desharp.Debug.Log(anyFunc);
Desharp.Debug.Log(anyFunc, Desharp.Level.EMERGENCY); // you can store into different files
it can save the log in nice HTML (or in TEXT format, configurable)
it's possible to write optionally in background thread (configurable)
it has options for max objects depth and max strings length (configurable)
it uses loops for iteratable objects and backward reflection for everything else,
indeed for anything you can find in .NET environment.
I believe it will help.
So far a simplest and tidiest way for me is a serializer from YamlDotNet package.
using YamlDotNet.Serialization;
List<string> strings=new List<string>{"a","b","c"};
new Serializer().Serialize(strings)
will give you
- a
- b
- c
A more comprehensive example is here https://dotnetfiddle.net/KuV63n
Today you don't even need an external dependency. You can just use the built-in Microsoft Json Serializer.
using System;
using System.Text.Json;
namespace MyCompany.Core.Extensions
{
public static class ObjectExtensions
{
public static string Dump(this object obj)
{
try
{
return JsonSerializer.Serialize(obj);
}
catch(Exception)
{
return string.Empty;
}
}
}
}
Notice that you can pass a JsonSerializerOptions parameter to further customize the serialization to your liking:
Let's say you want to write the JSON indented for easy reading... we'd use:
new JsonSerializerOptions { WriteIndented = true }
#######
Here's a good guide if you wish to migrate from NewtonSoft.Json to System.Text.Json:
Compare Newtonsoft.Json to System.Text.Json, and migrate to System.Text.Json
Based on #engineforce answer, I made this class that I'm using in a PCL project of a Xamarin Solution:
/// <summary>
/// Based on: https://stackoverflow.com/a/42264037/6155481
/// </summary>
public class ObjectDumper
{
public static string Dump(object obj)
{
return new ObjectDumper().DumpObject(obj);
}
StringBuilder _dumpBuilder = new StringBuilder();
string DumpObject(object obj)
{
DumpObject(obj, 0);
return _dumpBuilder.ToString();
}
void DumpObject(object obj, int nestingLevel)
{
var nestingSpaces = "".PadLeft(nestingLevel * 4);
if (obj == null)
{
_dumpBuilder.AppendFormat("{0}null\n", nestingSpaces);
}
else if (obj is string || obj.GetType().GetTypeInfo().IsPrimitive || obj.GetType().GetTypeInfo().IsEnum)
{
_dumpBuilder.AppendFormat("{0}{1}\n", nestingSpaces, obj);
}
else if (ImplementsDictionary(obj.GetType()))
{
using (var e = ((dynamic)obj).GetEnumerator())
{
var enumerator = (IEnumerator)e;
while (enumerator.MoveNext())
{
dynamic p = enumerator.Current;
var key = p.Key;
var value = p.Value;
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, key, value != null ? value.GetType().ToString() : "<null>");
DumpObject(value, nestingLevel + 1);
}
}
}
else if (obj is IEnumerable)
{
foreach (dynamic p in obj as IEnumerable)
{
DumpObject(p, nestingLevel);
}
}
else
{
foreach (PropertyInfo descriptor in obj.GetType().GetRuntimeProperties())
{
string name = descriptor.Name;
object value = descriptor.GetValue(obj);
_dumpBuilder.AppendFormat("{0}{1} ({2})\n", nestingSpaces, name, value != null ? value.GetType().ToString() : "<null>");
// TODO: Prevent recursion due to circular reference
if (name == "Self" && HasBaseType(obj.GetType(), "NSObject"))
{
// In ObjC I need to break the recursion when I find the Self property
// otherwise it will be an infinite recursion
Console.WriteLine($"Found Self! {obj.GetType()}");
}
else
{
DumpObject(value, nestingLevel + 1);
}
}
}
}
bool HasBaseType(Type type, string baseTypeName)
{
if (type == null) return false;
string typeName = type.Name;
if (baseTypeName == typeName) return true;
return HasBaseType(type.GetTypeInfo().BaseType, baseTypeName);
}
bool ImplementsDictionary(Type t)
{
return t is IDictionary;
}
}
Option 1: CSharpObjectFormatter from the Microsoft.CodeAnalysis.CSharp.Scripting.
C# REPL Command-Line Interface (CSI.EXE) and CSharpRepl use exactly this formatter.
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
var students = new[]
{
new Student
{
Id = 1,
Name = "Michael Hilus"
},
new Student
{
Id = 2,
Name = "Alicia Keys"
}
};
var formattedStudents = CSharpObjectFormatter.Instance.FormatObject(students);
Console.WriteLine(formattedStudents);
Output:
Student[2] { Student { Id=1, Name="Michael Hilus" }, Student { Id=2, Name="Alicia Keys" } }
But it's suitable for relatively simple objects, for example it can't dump the instance of System.Data.DataTable.
Option 2: vm.Aspects.Diagnostics.ObjectTextDumper - see documentation.
There is also very popular library:
ObjectDumper.NET, it's an open source, but not free for commercial use.

Categories

Resources