string.Concat, Mutable or Immutable? - c#

As I know string are immutable in C# unless StringBuilder class is used. The situation is, I have an ObjectResult(which implements IEnumerable) of string and I want to convert it to one single string object. There are two options here:
using string.Concat
var myDesiredData = string.Concat(TheObjectResult)
using StringBuilder
var sb = new StringBuiler();
foreach (var item in TheObjectResult)
{
sb.Append(item);
}
var myDesiredData = sb.ToString();
I'm not sure how the first option is working.

Both options are almost identical. This is how string.Concat(IEnumerable<string>) is implemented:
[ComVisible(false)]
public static String Concat(IEnumerable<String> values) {
if (values == null)
throw new ArgumentNullException("values");
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();
StringBuilder result = StringBuilderCache.Acquire();
using(IEnumerator<String> en = values.GetEnumerator()) {
while (en.MoveNext()) {
if (en.Current != null) {
result.Append(en.Current);
}
}
}
return StringBuilderCache.GetStringAndRelease(result);
}
I would go with string.Concat, because why write a method that already exists.

Related

Is there a way to deserialize a specific property of a YAML document?

In JSON.Net I can read a JSON file into a data structure and then only convert the properties that I'm interested in to objects. For example, if I have a JSON file like
{
"Bob": {
"Steve": 5
}
}
I can get the object like so:
class SteveObject {
public int Steve;
}
var jsonSerializer = JsonSerializer.Create()
var jsonFile = JObject.Parse(text);
vat steveObject = jsonFile["Bob"].ToObject<SteveObject>(jsonSerializer)
How do I do the same thing with YAMLDotNet or SharpYaml? I.e. if I have a YAML file like
Bob:
Steve: 5
How do I reconstruct the SteveObject from that without having to create the outer object?
Ok, I've written some code using SharpYaml that takes a simplified JPath query and returns the requested object.
public static T ReadPath<T>(Serializer serializer, string yaml, string path) {
var eventReader = new EventReader(new Parser(new StringReader(yaml)));
var streamReader = new MemoryStream(Encoding.UTF8.GetBytes(path ?? ""));
if ((char)streamReader.ReadByte() != '$')
throw new Exception();
if (streamReader.Position == streamReader.Length)
return serializer.Deserialize<T>(eventReader);
eventReader.Expect<StreamStart>();
eventReader.Expect<DocumentStart>();
while (streamReader.Position < streamReader.Length) {
if ((char)streamReader.ReadByte() != '.')
throw new Exception();
eventReader.Expect<MappingStart>();
var currentDepth = eventReader.CurrentDepth;
var nextKey = ReadPropertyName(streamReader);
if (string.IsNullOrEmpty(nextKey))
throw new Exception();
while (eventReader.Peek<Scalar>() == null || eventReader.Peek<Scalar>().Value != nextKey) {
eventReader.Skip();
// We've left the current mapping without finding the key.
if (eventReader.CurrentDepth < currentDepth)
throw new Exception();
}
eventReader.Expect<Scalar>();
}
return serializer.Deserialize<T>(eventReader);
}
public static string ReadPropertyName(MemoryStream stream) {
var sb = new StringBuilder();
while (stream.Position < stream.Length) {
var nextChar = (char) stream.ReadByte();
if (nextChar == '.') {
stream.Position--;
return sb.ToString();
}
sb.Append(nextChar);
}
return sb.ToString();
}
Given the above YAML the following works:
var t = "Bob:\r\n Point: [1.0, 2.0, 3.0]\r\n Steve: 5\r\n";
var serializer = new Serializer(settings);
var s = ReadPath<SteveObject>(serializer, t, "$.Bob"); // { "Steve": 5 }
var s2 = ReadPath<int>(serializer, t, "$.Bob.Steve"); // 5
Sure wish there were a library for this, though.

Confusion while working with lists and the "new" parameter

Well, i am new to C# and it seems that i got a problem right over here. I know the problem already, but I don't know how to solve it. I am simply overwriting the object i want to add to my list. Can anyone help me?
List<string> dataSet = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
dataSet.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(dataSet));
dataSet.Clear();
Console.WriteLine();
}
}
}
}
If I'm understanding your problem correctly your adding a list of strings to a dataset. You are then clearing that list. It looks to me that your adding a reference to a list, then your calling clear on that list. That is why your loosing your values. You need to add a copy of that list to the other dataset. Try the code below
List<string> dataSet = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
dataSet.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(dataSet.ToList()));
dataSet.Clear();
Console.WriteLine();
}
}
}
}
dataSet.ToList() will return a copy of the List instead of adding a reference to it, that way dataSet.Clear() won't clear the list you added to myData
I think one of the problems you may be having is that you've confused yourself a little by naming your List<T> a keyword. A DataSet is its own thing and makes this code very hard to read. First, let's clean that up:
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(theData)); //you're passing your source by reference.
theData.Clear();
Console.WriteLine();
}
}
}
}
You need to pass the value of your datasource, but a List<T> is a reference type.
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{
List<string> bindingdata = theData.ToList();
myData.Add(new DataSet(bindingData)); //you're passing
//your source by value, because it's using a new list.
theData.Clear(); //you can now safely do this.
Console.WriteLine();
}
}
}
}
All of that done, it still won't work, because DataSet doesn't have a constructor that accepts a List<string>. You need to change myData.Add(new DataSet(theData)) to the actual constructor. Interestingly, your DataSet may be the poor choice for what you're doing here. Unless you are using multiple tables, which you don't appear to be, you're better off using just one DataTable.
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{ //we don't need this now.
//List<string> bindingdata = theData.ToList();
//make myData a DataSet so you can use datatables
// instead of attempting IEnumerable<DataSet>
DataTable foo = new DataTable("Foo");
foreach(string s in theData)
{
var y = foo.NewRow();
y[0] = s;
foo.Rows.Add(y);
}
myData.Add(foo);
theData.Clear(); //you can now safely do this.
Console.WriteLine();
}
}
}
}
Even with all of that, you'd be saving yourself a lot of trouble if you look into the DataTable and StringBuilder classes. There are better ways to do all of this than you're attempting.

How do I refactor a ReadLine loop to Linq

I'd like to make below code cleaner (in the eye of the beholder).
var lines = new StringReader(lotsOfIncomingLinesWithNewLineCharacters);
var resultingLines = new List<string>();
string line;
while( (line = lines.ReadLine() ) != null )
{
if( line.Substring(0,5) == "value" )
{
resultingLines.Add(line);
}
}
to something like
var resultingLinesQuery =
lotsOfIncomingLinesWithNewLineCharacters
.Where(s=>s.Substring(0,5) == "value );
Hopefully I have illustrated that I'd prefer to not have the result as a list (to not fill up memory) and that StringReader is not mandatory.
There is the naïve solution to create an extension and move the ReadLine there but I have a feeling there might be a better way.
Basically you need a way of extracting lines from a TextReader. Here's a simple solution which will only iterate once:
public static IEnumerable<string> ReadLines(this TextReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
You could use that with:
var resultingLinesQuery =
new StringReader(lotsOfIncomingLinesWithNewLineCharacters)
.ReadLines()
.Where(s => s.Substring(0,5) == "value");
But ideally, you should be able to iterate over an IEnumerable<T> more than once. If you only need this for strings, you could use:
public static IEnumerable<string> SplitIntoLines(this string text)
{
using (var reader = new StringReader(text))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Then:
var resultingLinesQuery =
lotsOfIncomingLinesWithNewLineCharacters
.SplitIntoLines()
.Where(s => s.Substring(0,5) == "value");

Generate POCO from dynamic in C#

Is there anything built into .NET 4.5 that will generate a string C# POCO from a dynamic with all auto-implemented properties?
If not, is there anything built into .NET that will give you (something like a) List<KeyValuePair<string, Type>> so that we can generate a POCO according to the pseudo-code:
foreach (var kvp in list)
{
builder.AppendFormat("public {0} {1} {{ get; set; }}", kvp.Value, kvp.Key);
}
Finally, are there any well-known libraries that can assist with this sort of very basic code generation?
You can use compileassemblyfromsource to compile your string,
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.compileassemblyfromsource(v=vs.110).aspx
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var cp = new CompilerParameters()
{
GenerateExecutable = false,
GenerateInMemory = true
};
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
// The string can contain any valid c# code
// A valid class need to be created with its own properties.
var s = "public class POCOClass{ public int ID {get {return 1;}} }";
// "results" will usually contain very detailed error messages
var results = csc.CompileAssemblyFromSource(cp, s);
var type = results.CompiledAssembly.GetType("POCOClass");
var obj = (dynamic)Activator.CreateInstance(type);
var output = obj.ID;
// or
var output_ = obj.GetType().GetProperty("ID").GetValue(obj, null);
You need to define a class for your properties such as called POCOClass.
EDIT:
public static T CopyObjectFromExpando<T>(this object s) where T : class
{
var source = (ExpandoObject)s;
// Might as well take care of null references early.
if (source == null)
{
throw new ArgumentNullException("s");
}
var propertyMap = typeof(T).GetProperties().ToDictionary(p => p.Name.ToLowerInvariant(), p => p);
var destination = Activator.CreateInstance<T>();
// By iterating the KeyValuePair<string, object> of
// source we can avoid manually searching the keys of
// source as we see in your original code.
foreach (var kv in source)
{
PropertyInfo p;
if (propertyMap.TryGetValue(kv.Key.ToLowerInvariant(), out p))
{
var propType = p.PropertyType;
if (kv.Value == null)
{
if (!propType.IsNullable() && propType != typeof(string))
{
// Throw if type is a value type
// but not Nullable<>
throw new ArgumentException("not nullable");
}
}
else if (propType.IsEnum)
{
var enumvalue = Enum.ToObject(propType, kv.Value);
p.SetValue(destination, enumvalue, null);
continue;
}
else if (propType == typeof(bool) && kv.Value.GetType() != typeof(bool))
{
var boolvalue = Convert.ToBoolean(kv.Value);
p.SetValue(destination, boolvalue, null);
continue;
}
else if (propType.IsNullable())
{
var nullType = Nullable.GetUnderlyingType(propType);
var value = Convert.ChangeType(kv.Value, nullType);
p.SetValue(destination, value, null);
continue;
}
else if (kv.Value.GetType() != propType)
{
// You could make this a bit less strict
// but I don't recommend it.
throw new ArgumentException("type mismatch");
}
p.SetValue(destination, kv.Value, null);
}
}
return destination;
}
ImpromptuInterface, open source on Nuget
PM> Install-Package ImpromptuInterface
Has ActLikeProperties
by using ImpromptuInterface
Impromput.ActLikeProperties(dynObj, list.ToDictionary(k=>k.Key,v=>v.Value))
This emits a poco dlr proxy around the dynObj
The intent was to be able to bridge simple dynamic objects (like expando) to old code that uses reflection. But generally it's better to do what ImpromptuInterface's main function, which is wrap dynamic objects with statically declared interfaces.

how to execute string path on dynamic type?

Is it possible to execute string path on dynamic type?
For example having dynamic type we can write
dynamic d = myObj;
var v = d.MyMethod(1,"text").SomeProperty.Name
Now imagine I have string path
string path = "MyMethod(1,\"text\").SomeProperty.Name";
var v = d. //How to applay string path to this type?
I have solution using extension method and reflection, you need to optimize and test for different scenario.
EDIT
Still dirty code, but supports overloaded method now. I will try to do code clean up and use regex for effective and cleaner solution
You can specify data types for parameters now to the eval method.
string epath = "GetName(System_String: ding dong, System_Int32:1).name";
MyClass cls = new MyClass();
var v = cls.Eval(epath);
Note underscore in type names. This should work without mentioning datatypes if method are not overloaded. Current restriction, you cannot use colon or comma inside string parameter value. :(
Call like var v = d.Execute(path)
public static object Eval(this object instance, string path)
{
string[] cmd = path.Split('.');
string subString = cmd[0];
object returnValue = null;
Type t = instance.GetType();
if (subString.Contains("("))
{
string[] paramString = subString.Split('(');
string[] parameters = paramString[1].Replace(")", "").Split(new Char[]{','},StringSplitOptions.RemoveEmptyEntries);
bool hasNoParams = parameters.Length == 0;
List<Type> typeArray = null;
if (hasNoParams) typeArray = new List<Type>();
foreach (string parameter in parameters)
{
if (parameter.Contains(":"))
{
if (typeArray == null) typeArray = new List<Type>();
string[] typeValue = parameter.Split(':');
Type paramType = Type.GetType(typeValue[0].Replace('_','.'));
typeArray.Add(paramType);
}
}
MethodInfo info = null;
if (typeArray == null)
info = t.GetMethod(paramString[0]);
else
info = t.GetMethod(paramString[0], typeArray.ToArray());
ParameterInfo[] pInfo = info.GetParameters();
List<object> paramList = new List<object>();
for (int i = 0; i < pInfo.Length; i++)
{
string currentParam = parameters[i];
if (currentParam.Contains(":"))
{
currentParam = currentParam.Split(':')[1];
}
ParameterInfo pram = pInfo[i];
Type pType = pram.ParameterType;
object obj = Convert.ChangeType(currentParam, pType);
paramList.Add(obj);
}
if (info == null) returnValue = null;
else
returnValue = info.Invoke(instance, paramList.ToArray());
}
else
{
PropertyInfo pi = t.GetProperty(subString);
if (pi == null) returnValue = null;
else
returnValue = pi.GetValue(instance, null);
}
if (returnValue == null || cmd.Length == 1)
return returnValue;
else
{
returnValue = returnValue.Eval(path.Replace(cmd[0] + ".", ""));
}
return returnValue;
}
It seems you would need an eval function and C# has none but if this third party C# eval implementation can handle dynamic it might solve your problem.

Categories