c# dynamically pass string method at run time for string manipulation - c#

How do I dynamically pass string methods to be applied to strings at run time.
ex.
Private String Formatting(String Data, String Format)
When we pass String S1 = "S1111tring Manipulation" and format = Remove(1,4) - behind the scenes it becomes S1.Remove(1,4) resulting in "String Manipulation"
or if we pass String S1 = "S1111tring Manipulation" and format = ToLower() behind the scene it becomes S1.ToLower() resulting in "s1111tring manipulation"
I should be able to pass any valid method like PadLeft(25,'0'), PadRight, Replace etc...
I would appreciate a complete example
This is what I have tried and it does not work
using System.Reflection;
string MainString = "S1111tring Manipulation";
string strFormat = "Remove(1, 4)";
string result = DoFormat(MainString, strFormat);
private string DoFormat(string data, string format)
{
MethodInfo mi = typeof(string).GetMethod(format, new Type[0]);
if (null == mi)
throw new Exception(String.Format("Could not find method with name '{0}'", format));
return mi.Invoke(data, null).ToString();
}
throws an error (Could not find method with name 'Remove(1, 4)') - so I am not sure how to proceed

Have a look at Reflection. You can essentially implement what you're describing using it save for the parsing of the user supplied text.
The smiple example you've used there would be something like,
var method = "ToLower()";
var methodInfo = typeof(String).GetMethod(method);
var string = "foo";
string.GetType().InvokeMember(....);

Consider using an enum instead of the second string parameter. It will be helpful as for type safety.
public enum StringManipulationType
{
ToLower,
ToUpper
}
and then rewrite the manipulation method you had with the following:
private string Formatting(String data, StringManipulationType manipulationType)
{
switch (manipulationType)
{
case StringManipulationType.ToLower:
return data.ToLower();
case StringManipulationType.ToUpper:
return data.ToUpper();
case default:
throw new ArgumentException();
}
}
In all the places where you had the earlier "string parameter", change it with enum like this:

Related

Parsing string for object property

How do I convert "Account" in to
<Account>
or "Object Name" into
<Object with matching name>
I'm attempting to parse a string and replace values in a string with object properties.
This is similar to what we do with form letters we send out, you know the Dear [Customer Name], thank you for purchasing [Some Item]. In this case the fields in the letter aren't setup by me and I need a means of converting what is in the text block into an object property.
Currently I'm using a code in the string {value=x; id=y ;property=z} and running that through a switch case to convert it into an object. Then replacing the {} with the object properties.
for example "some random string {value=1; id=1; property=Name} continued random string"
I parse the string to locate {value=, on a hit it runs through a switch case, where on case 1: return Account(ID = 1). The I grab Account.Name
Once I have that I put it in a text box so the user can validate that it is correct before generating a final document to be sent out.
Is there a way to have {Object.Property} in the string then use reflection to convert that string value to the object value?
Get property value from string using reflection
I used the above in other instances, but that requires that I have the object.
public static List<string> GetClassProperties(string className, IEnumerable<string> propertiesToExclude = null)
{
Type theType = Type.GetType(className);
if (theType == null)
{
return null;
}
}
This'll get you the Object type, you do need the full namespace for it to work, can't just be "Account" would need to be Project.Models.Account

How to parse a Ternary Statement in C#

I'm accepting an input string that I want to be a ternary statement that works on strings. So my method signature would look like this:
public string Parse(string value, string ternaryStatement)
and there parameters would give these results:
Parse(null, "=?It's Null:It's Not Null") == "It's Null" // Empty string would
Parse("", "=?It's Null:It's Not Null") == "It's Null" // be same as null
This example is fairly simple, Split the string first by '?' then by ':'
But of course I need a method to handle escape characters, "\", "\?" and ":", where "\" is valid anywhere, "\?" would only be valid before the first unescaped "?" and ":" would only be valid after that same "?".
Parse(#"\?\", #"=\\\?\\?\:Match\::\:No Match\:") == ":Match:"
Parse(#"\?\", #"!=\\\?\\?\:No Match\::\:Match\:") == ":Match:"
But this is really complicated. I believe I can perform it using regular expressions, but that just creates another problem since this is well beyond my limited understanding of regular expressions. What's the best way to tackle this problem?
Edit 1
Some of the background: I'm storing a format for a URL in a database config table (It's actually Dynamics 365 for Customer Engagement, but that doesn't matter at this point). The format is stored as strings, and the parameters that are required are defined in code. So generally it looks like this:
Format: "https://something.com?Foo={0}&Bar={1}"
Description: "0 - Foo, 1 - Bar"
where the description is used both for the person that is formatting the url, and the developer that needs to know how to structure the format statement.
The problem I'm running into right now is that I have a url that requires at least one of two different parameters. If one of the values is null or empty, it will error if included in the url. So I need a way of saying, if Foo is null or Bar is null, don't include the name or &. Ideally I'd like to implement this like this:
"https://something.com?{0:=?:Foo={{0}}}&}{1:=?:Bar={{1}}}}"
So if Foo is null and Bar is "Bar" the output would be
"https://something.com?Bar=Bar"
I could also see this being used if we need to switch between a 0/1 for a boolean to true/false without having to change code:
"https://something.com?{0:=0?false:true}"
The two regexes should be:
Regex rx = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*)\?");
Regex rx2 = new Regex(#"(?<=(?:^|[^\\])(?:\\\\)*):");
Use them like:
var m = rx.Match(str);
if (m.Success)
{
int ix = m.Index;
}
The main point of the two rx is that the searched string (\? or :) must be preceded by
(?<=(?:^|[^\\])(?:\\\\)*)
that is the beginning of the string ^ or not a \ ([^\\]) plus zero or an even number of \\ that is (?:\\\\)*.
A all-in-one regex is:
Regex rx = new Regex(#"^(?<operator>=|!=|<=|>=|<|>)(?<cmp>(?:(?:\\.)|[^?:])*)\?(?<true>(?:(?:\\.)|[^?:])*):(?<false>(?:(?:\\.)|[^?:])*)$");
if (m.Success)
{
string op = m.Groups["operator"].Value;
string cmp = m.Groups["cmp"].Value;
string true1 = m.Groups["true"].Value;
string false1 = m.Groups["false"].Value;
}
In op you'll get the comparison operator used, in cmp the comparand, in true1 and false1 the true and false strings. If !m.Success then the string isn't correctly formatted. Comprehending the regex is left as a simple exercise for the reader (unless you comprehend a regex, you shouldn't ever use it, because before or later you'll have to modify it/fix it/debug it)
Solution to returning different values based on input string
Why do you need to pass in a ternary statement / wouldn't this make more sense?
string Parse(string value, string returnIfNull, string returnIfNotNull)
{
return string.IsNullOrEmpty(value) ? returnIfNull: returnIfNotNull;
}
Console.WriteLine(Parse("", "treat as null", "not expected"));
Console.WriteLine(Parse("hello", "not expected", "this value's not null"));
Parsing a ternary string for values
However, if you really need to do this, you could use something like the below:
private static readonly Regex TernaryParserRegex = new Regex(
#"^=\?(?<ifNull>(?:\\(\\\\)*:|[^:])*)(?<!\\):(?<ifNotNull>(?:\\(\\\\)*:|[^:])*)$"
/* , RegexOptions.Compiled //include this line for performance if appropriate */
);
private string[] ParseTernaryString (string ternaryStatement)
{
var results = TernaryParserRegex.Match(ternaryStatement);
if (results.Success)
{
string[] returnVal = {
results.Groups["ifNull"].Value
,
results.Groups["ifNotNull"].Value
};
return returnVal;
}
else
{
throw new Exception("Invalid Ternary Statement"); //use an appropriate exception type here; or have the function return `{null,null}` / some other default as appropriate
}
}
public string Parse(string value, string ternaryStatement)
{
var returnValues = ParseTernaryString(ternaryStatement);
return string.IsNullOrEmpty(value) ? returnValues[0]: returnValues[1];
}
//Example Usage:
Console.WriteLine(Parse("", "=?treat as null:not expected"));
Console.WriteLine(Parse("hello", "=?not expected:this value's not null"));
An explanation of the regex & additional examples are available here:
https://regex101.com/r/YJ9qd3/1
Appending non-null/blank values to a URL's Query String
public void Main()
{
var url = "https://example.com?something=keepMe&foo=FooWasHere&bar=swapMeQuick";
var dict = new System.Collections.Generic.Dictionary<string, string>();
dict.Add("foo", null);
dict.Add("bar", "hello");
dict.Add("boo", "new");
Console.WriteLine(CreateUri(url, dict).ToString());
}
Uri CreateUri(string uri, IDictionary<string, string> parameters)
{
return CreateUri(new Uri(uri), parameters);
}
Uri CreateUri(Uri uri, IDictionary<string, string> parameters)
{
var query = HttpUtility.ParseQueryString(uri.Query); //https://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx; though returns HttpValueCollection
foreach (string key in parameters.Keys)
{
if (string.IsNullOrEmpty(parameters[key]))
{ //parameter is null or empty; if such a parameter already exists on our URL, remove it
query.Remove(key); //if this parameter does not already exist, has no effect (but no exception is thrown)
}
else
{ //parameter has a value; add or update the query string with this value
query.Set(key, parameters[key]);
}
}
var builder = new UriBuilder(uri);
builder.Query = query.ToString();
return builder.Uri;
}

Format placeholder in string.format to pick substring of an argument

Is there a format string for the C# string.Format method that picks a substring from the corresponding argument? Like so:
var lang1 = "EN";
var lang2 = "FR";
var shortFormat = "Foo-{0:0-0}.pdf";
var longFormat = "Foo-{0:0-1}.pdf";
string.Format(shortFormat, lang1) // Foo-E.pdf
string.Format(shortFormat, lang2) // Foo-F.pdf
string.Format(longFormat, lang1) // Foo-EN.pdf
string.Format(longFormat, lang2) // Foo-FR.pdf
To anticipate a few comments: Yes, I know the Substring method. I have also read that string.Format is slower than a simple Substring. The example above is heavily simplified. Imagine that the string.Format statement resides in one place, while the lang1/lang2 argument is an input from another place and the shortFormat/longFormat is defined in a resx file.
That is, in the place where the format is to be defined we don't know anything about the value being formatted (lang1/lang2 in the example) nor do we have any means to execute C# code. Hence we can't call any method such as Substring on the value. At the place where the formatting code runs, in turn, we take the format as a parameter, so we can't simply perform a Substring on the value because we don't know whether the format requires it or not (except if we inspect the format).
No, the string.Format does not have this feature, which is better explained here: Can maximum number of characters be defined in C# format strings like in C printf?
If you don't want to use Substring I would create an extension class for string like this: http://msdn.microsoft.com/en-us/library/bb311042.aspx
namespace CustomExtensions
{
public static class StringExtension
{
public static string ShortFormat(this string str)
{
// manipulate and return str here
}
public static string LongFormat(this string str)
{
// manipulate and return str here
}
}
}
XSLT formatting can be an option: user gets ability to provide almost everything in configuration file and even execute custom c# code in your domain if it is required.
Please also consider that changes of format can be restricted to relatively small amount of actions: crop, pad or insert one or two things in some positions. Each one can be set as individual function and provided with own parameters.
There are two ways to provide custom formatting. You can either implement IFormattable on a custom type to control how that type is always formatted, or implement IFormatProvider to override how other types are formatted in specific cases.
In your case I would suggest creating a new type to encapsulate how your software deals with language codes;
public struct LanguageCode : IFormattable {
public readonly string Code;
public LanguageCode(string code) {
Code = code;
}
public override string ToString()
=> this.ToString("L", CultureInfo.CurrentCulture);
public string ToString(string format)
=> this.ToString(format, CultureInfo.CurrentCulture);
public string ToString(string format, IFormatProvider provider){
if (String.IsNullOrEmpty(format))
format = "L";
if (provider == null)
provider = CultureInfo.CurrentCulture;
switch (format.ToUpperInvariant()){
case "L": // Long
return Code.ToString(provider);
case "S": // Short
return Code.SubString(0,1).ToString(provider);
default:
throw new FormatException($"The {format} format string is not supported.");
}
}
public static implicit operator LanguageCode(string code)
=> new LanguageCode(code);
public static implicit operator string(LanguageCode language)
=> language.Code;
}
Then from your example;
var lang1 = (LanguageCode)"EN";
LanguageCode lang2 = "FR";
var shortFormat = "Foo-{0:S}.pdf";
var longFormat = "Foo-{0:L}.pdf";

How to read dicom tag value using openDicom.net in C#?

I'm reading dicom tags using openDicom.net like this:
string tag = "";
string description = "";
string val_rep = "";
foreach (DataElement elementy in sq)
{
tag = elementy.Tag.ToString();
description = elementy.VR.Tag.GetDictionaryEntry().Description;
val_rep = elementy.VR.ToString();
}
How can I read dicom tag values?
the value.ToString() method isn't implemented. Implement your own method in Value.cs and you will get a value for "Value".
For example (only strings and numeric values):
public override string ToString()
{
return valueList[0].ToString();
}
I'm assuming that sq is a Sequence...
I've not worked with openDicom, but I'm pretty sure what you're doing there isn't going to yield the results you want.
You have a single tag, description, and val_rep variable, but you're filling them using a foreach, meaning the last DataElement in the Sequence will be the only values you retrieve. You would achieve the same effect by using:
string tag = sq[sq.Count - 1].Tag.ToString();
string description = sq[sq.Count -1].VR.Tag.GetDictionaryEntry().Description;
string val_rep = sq[sq.Count - 1].VR.ToString();
Thus retrieving the last set of values from the Sequence. I believe you'll find that if you step through the foreach as it executes, it will be loading all the different DataElements contained in your DICOM file.
Feel free to return a comment or post more information in your original post if I'm way off base here.
The tag value is retrieved as an array of generic object from the 'Value' member in 'DataElement' using the 'ToArray()' overriding method in the 'Value' class.
cniDicomTagList = new List<CNIDicomTag>();
foreach (DataElement element in sq)
{
string tag = element.Tag.ToString();
string description = element.VR.Tag.GetDictionaryEntry().Description;
object[] valueArr = element.Value.ToArray();
cniDicomTagList.Add(new CNIDicomTag(tag, description, valueArr));
}

What is the best way of performing a string concatenation in making a dynamic URL?

I am constructing a URL at runtime. So far I have done like
public string BuildURLAndNavigate(CodeType codeType)
{
string vURL = string.Empty;
string mstrNavServer = "http://some.com/nav";
vURL = ConcatenateString(mstrNavServer , "/somepage.asp?app=myapp");
//Build Code Type
switch (codeType)
{
case CodeType.Series:
vURL = ConcatenateString(vURL , "&tools=ser");
break;
case CodeType.DataType:
vURL = ConcatenateString(vURL , "&tools=dt");
break;
}
//build version
string VER_NUM = "5.0";
vURL = ConcatenateString(vURL , ConcatenateString("&vsn=" , VER_NUM));
return vURL;
}
private string ConcatenateString(string expression1, string expression2)
{
return string.Concat(expression1 + expression2);
}
But I am not happy with the one I am doing.
I am sure that there is definitely a best practice / better approach than this.
Kindly help me out in guiding for the same.
Thanks
You could use a StringBuilder:
public string BuildURLAndNavigate(CodeType codeType)
{
StringBuilder vURL = new StringBuilder();
vURL.Append("http://some.com/nav");
vURL.Append("/somepage.asp?app=myapp");
//Build Code Type
switch (codeType)
{
case CodeType.Series:
vURL.Append("&tools=ser");
break;
case CodeType.DataType:
vURL.Append("&tools=dt");
break;
}
//build version
string VER_NUM = "5.0";
vURL.AppendFormat("&vsn={0}", VER_NUM);
return vURL.ToString();
}
Never build urls using strings, string builders, string concatenations.
You could start by defining a custom collection which will take care of properly URL encoding any value being added to it:
public class HttpNameValueCollection : NameValueCollection
{
public override void Add(string name, string value)
{
base.Add(name, HttpUtility.UrlEncode(value));
}
public override string ToString()
{
return string.Join("&", Keys.Cast<string>().Select(
key => string.Format("{0}={1}", key, this[key])));
}
}
And then simply:
public string BuildURLAndNavigate()
{
var uriBuilder = new UriBuilder("http://some.com/nav/somepage.asp");
var values = new HttpNameValueCollection();
values.Add("app", "myapp");
switch (codeType)
{
case CodeType.Series:
values.Add("tools", "ser");
break;
case CodeType.DataType:
values.Add("tools", "dt");
break;
}
// You could even do things like this without breaking your urls
values.Add("p", "http://www.example.com?p1=v1&p2=v2");
string VER_NUM = "5.0";
values.Add("vsn", VER_NUM);
uriBuilder.Query = values.ToString();
return uriBuilder.ToString();
}
Like Saxon Druce said: You could use a StringBuilder, but, depending on CodeType values, you could eliminate the switch too:
public string BuildURLAndNavigate(CodeType codeType)
{
StringBuilder vURL = new StringBuilder();
vURL.Append("http://some.com/nav");
vURL.Append("/somepage.asp?app=myapp");
//Build Code Type
vURL.Append(codeType == CodeType.Series ? "&tools=ser" : "&tools=dt");
//build version
string VER_NUM = "5.0";
vURL.AppendFormat("&vsn={0}", VER_NUM);
return vURL.ToString();
}
Do
return string.Concat(expression1, expression2);
not
return string.Concat(expression1 + expression2);
wouldn't the right way to do that be to use the Uri-class or the UriBuilder class?
for example the Uri ctor overload Uri(Uri, string):
public Uri(
Uri baseUri,
string relativeUri
);
Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");
Console.WriteLine(myUri.ToString());
http://msdn.microsoft.com/en-us/library/aa332624(v=VS.71).aspx
What you are doing is fine - it is simple and understandable. Anyone who reads the code can understand what you are doing.
In terms of performance - you are not doing much string manipulation, so unless you are building huge strings or doing this operation thousands of times a minute, you will not gain much by using StringBuilder. Before optimizing this code, test its performance. You will probably find that there are other bigger bottlenecks to work on first.
The only real comment I have is that your ConcatenateString function seems superfluous. It is not really adding anything to the code and all the call to it can simply be replaced by string.Concat. As mentioned in the answer from #abatishchev, you should be using (str1, str2) not (str1 + str2), as that defeats the reason for the call.
Yes, StringBuilder is the best solution here. You can find more information on MSDN page: http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx
StringBuilder contains very usefull methods:
StringBuilder.Append
Appends information to the end of the current StringBuilder.
StringBuilder.AppendFormat
Replaces a format specifier passed in a string with formatted text.
StringBuilder.Insert
Inserts a string or object into the specified index of the current StringBuilder.
StringBuilder.Remove
Removes a specified number of characters from the current StringBuilder.
StringBuilder.Replace
Replaces a specified character at a specified index.
I'd personally be inclined to just string.format something like that:
public string BuildURLAndNavigate(CodeType codeType)
{
string vURL = "http://some.com/nav/somepage.asp?app=myapp&tools={0}&vsn={1}";
string codevalue = "";
//Build Code Type
switch (codeType)
{
case CodeType.Series:
codevalue = "ser";
break;
case CodeType.DataType:
codevalue = "dt";
break;
}
//build version
string version = "5.0";
return string.Format(vURL, codevalue, version);
}
}
Apologies if there are any mistakes in that, I wasn't in VS when writing it so there might be a few typos I didn't notice - you should get the idea though.
The reason I like this method is because you can immediately see what your url's overall form is which can make it a bit easier to understand roughly what the url being returned is.
In order to keep all variables at one place, could we use following solution
public string BuildURLAndNavigate(CodeType codeType)
{
//code here - switch statement to calculate the codeValue
//Anonymous type - To keep all variables at one place
var urlComponents = new {
server = "http://some.com/nav",
pageName="/somepage.asp?app=myapp",
codevalue = "", //Replace with the actual value calculated in switch statement
versionPart="&vsn=",
version = "5.0"
};
StringBuilder vURL = new StringBuilder();
vURL.Append(urlComponents.server);
vURL.Append(urlComponents.pageName);
vURL.Append(urlComponents.codevalue);
vURL.Append(urlComponents.versionPart);
vURL.Append(urlComponents.version);
return vURL.ToString();
}

Categories

Resources