I have a method where I'm currently passing in both someObject and nameof(someObject) since I'm using this method many times I'm wanting to simplify my code by finding a way to only pass in the object once but if I do the nameof() inside the method I'm obviously not going to get the name I want.
What I have currently is something like this:
public record ResourceObject
{
public string Name { get; init; }
public byte[] File { get; init; }
public string Content { get; init; }
public ResourceObject(string name, byte[] file)
{
Name = name;
File = file;
Content = Encoding.Default.GetString(file);
}
}
Where the use looks like this:
var test = new ResourceObject(nameof(Properties.Resources.SomeResource), Properties.Resources.SomeResource)
Ideally, I'd like to get the use to look like this(I don't think this is possible):
var test = new ResourceObject(Properties.Resources.SomeResource)
I did find a post that shows getting the name like this but then I can't get the object itself:
public record ResourceObject
{
public string Name { get; init; }
public byte[] File { get; init; }
public string Content { get; init; }
private ResourceObject(string name, byte[] file)
{
Name = name;
File = file;
Content = Encoding.Default.GetString(file);
}
public static ResourceObject New<T>(Expression<Func<T>> property)
{
var name = (property.Body as MemberExpression).Member.Name;
var file = ???; //I can't figure out how to get the object itself here
return new(name, file);
}
}
The use for that looks like this (which would be great improvement over what I if I could get it to work):
var test = ResourceObject.New(() => Resources.TradeEngineSettings_Base);
This is what ultimately worked for me.
public record ResourceObject<T>
{
public string Name { get; }
public T Object { get; }
public string Content { get; }
public ResourceObject(T #object, [CallerArgumentExpression(nameof(#object))] string name = null)
{
Name = name.Split('.', StringSplitOptions.RemoveEmptyEntries)[^1];
Object = #object;
if (#object?.GetType() == typeof(byte[]))
{
Content = Encoding.Default.GetString(#object as byte[] ?? Array.Empty<byte>());
}
}
}
To keep Name more similar to a nameof() behavior, name is split on '.' and only takes the last item in the array.
Related
I have a model with a bunch of fields defined like this:
public class Transaction
{
public DateTime R03DateFrom { get; set; }
public DateTime? R03DateTo { get; set; }
[MaxLength(80)]
public string R03Place { get; set; }
[MaxLength(20)]
public string R03Code { get; set; }
// And so on....
}
At a certain point I need to export some of this data to fixed width files, and if a string has a MaxLength of x, then in the output file it should always be output right-padded with spaces up to x characters.
I'm hoping to re-use the fact that the string's MaxLength is always defined in the Model in order to flow this information through to the export.
At the moment, a typical export row function looks like this (ExDateTime is an extension method that formats the date in yyyyMMddhhmm format):
private string GetR03Row()
{
return GetRowCode() + "03" +
R03DateFrom.ExDateTime() +
R03DateTo.ExDateTime() +
(R03Place??"").PadRight(80) +
(R03Code??"").PadRight(20);
}
I'd like to replace the line
(R03Place??"").PadRight(80)
with something that uses the MaxLength attribute.
Every string will have a MaxLength.
UPDATE:
I've taken the suggestion below and turned it into an Extension Method:
public static string PadToMax<T>(this string source, string propName)
{
PropertyInfo[] props = typeof(T).GetProperties();
var found = props.Where(m => m.Name.Equals(propName));
if (!found.Any()) return source;
var propertyInfo = found.First();
var attrs = propertyInfo.GetCustomAttributes(false);
if (!attrs.Any()) return source;
foreach (var maxLengthAttribute in attrs.OfType<MaxLengthAttribute>())
{
return (source??"").PadRight(maxLengthAttribute.Length);
}
return source;
}
which allows me to use this syntax to achieve what I want:
// (R03Place??"").PadRight(80) turns into
R03Place.PadToMax<Transaction>(nameof(R03Place))
This is fine. I'd love it if I could change the extension method to somehow work out the "Transaction" type and the name of the source string variable. But thi is close enough.
What Olivier proposed should work.
Here another way:
private static void Main(string[] args)
{
var maxLength = GetMaxLengthAttributeValue<Transaction>("R03Place");
Console.WriteLine("R03Place = {0}",maxLength);
maxLength = GetMaxLengthAttributeValue<Transaction>("R03Code");
Console.WriteLine("R03Place = {0}",maxLength);
Console.ReadLine();
}
public static int? GetMaxLengthAttributeValue<T>(string propertyName)
{
PropertyInfo[] props = typeof (T).GetProperties();
var found = props.Where(m => m.Name.Equals(propertyName));
if (!found.Any()) return null;
var propertyInfo = found.First();
var attrs = propertyInfo.GetCustomAttributes(false);
if (!attrs.Any()) return null;
foreach (object attr in attrs)
{
MaxLengthAttribute maxLengthAttribute = attr as MaxLengthAttribute;
if (maxLengthAttribute != null)
{
return maxLengthAttribute.Length;
}
}
return null;
}
Put the method in a helper class:
//You can use it as:
(R03Place??"").PadRight(YourHelper.GetMaxLengthAttributeValue<Transaction>("R03Place").Value);
// with C# 6, you don't have to hard code the property name
(R03Place??"").PadRight(YourHelper.GetMaxLengthAttributeValue<Transaction>(nameof(R03Place)).Value);
Could you save the maxlength as a private field?
public class Transaction
{
private int maxLengthPlace = 80
public DateTime R03DateFrom { get; set; }
public DateTime? R03DateTo { get; set; }
[MaxLength(maxLengthPlace)]
public string R03Place { get; set; }
[MaxLength(20)]
public string R03Code { get; set; }
// And so on....
}
and then use the same field later on?
(R03Place??"").PadRight(maxLengthPlace)
I am creating a webservice to interact with a JSON API.
This API needs me to set a root element in the string, but I just cannot get this to happen.
The place where it all happens - right now just made to just show me the json output:
public static string CreateServiceChange(ServiceChange change)
{
string json = JsonConvert.SerializeObject(change);
return json;
}
This is the ServiceChange class:
public class ServiceChange
{
[JsonProperty("email")]
public string requesterEmail { get; set; }
[JsonProperty("description_html")]
public string descriptionHtml { get; set; }
[JsonProperty("subject")]
public string subject { get; set; }
[JsonProperty("change_type")]
public int changeType { get; set; }
}
And the method binding those two together:
public string copyTicketToChange(int ticketId)
{
HelpdeskTicket.TicketResponseActual ticket = getHelpdeskTicket(ticketId);
ServiceChange change = new ServiceChange();
change.descriptionHtml = ticket.Response.DescriptionHtml;
change.requesterEmail = ticket.Response.Email;
change.subject = ticket.Response.Subject;
change.changeType = 1;
string Response = Dal.CreateServiceChange(change);
return Response;
}
The json output looks like this right now:
{"email":"test#test.com","description_html":"This is a test","subject":"Testing","change_type":1}
And the expected output:
{ "itil_change": {"email":"test#test.com","description_html":"This is a test","subject":"Testing","change_type":1}}
How can I achieve this?
Wrap your ServiceChange into another object and serialize it:
public class ServiceChangeWrapper
{
public ServiceChange itil_change { get; set; }
}
...
public static string CreateServiceChange(ServiceChange change)
{
ServiceChangeWrapper wrapper = new ServiceChangeWrapper { itil_change = change};
string json = JsonConvert.SerializeObject(wrapper);
return json;
}
This question already has answers here:
Return multiple values to a method caller
(28 answers)
Closed 8 years ago.
I have a question in returning two values from a method.
I have a method from which I have to return two values. I did like this.
public string fileName(string rate,out string xmlName,out string xsdName)
{
if(rate=="2013")
{
xmlName="abc";
xsdName="def";
}
if(rate=="2014")
{
xmlName="pqr";
xsdName="xyz";
}
//stmt 1 (Here it is asking to give default values for xmlname and xsdName.But i dont want to give any default values.)
}
Now in another class I have to call this function and assign these values of xmlname and xsdName in that class. How can I do this?
public OtherClass fileName(string rate)
{
OtherClass obj = new OtherClass();
if (rate == "2013")
{
obj.xmlName = "abc";
obj.xsdName = "def";
}
if (rate == "2014")
{
obj.xmlName = "pqr";
obj.xsdName = "xyz";
}
return obj;
}
public class OtherClass
{
public string xmlName { get; set; }
public string xsdName { get; set; }
}
You'd use it like this:
string xmlName;
string xsdName;
fileName("2014", out xmlName, out xsdName);
Your fileName() method can have a return type of void since you're not technically returning anything from the function.
The best concept would be using the Objective-Oriented programming. Create a class that has two properties, xmlName and xsdName. Then return a new instance of the class from the method.
The code below should give you an idea.
Class implementation in file XmlFile.cs
public class XmlFile
{
public string XmlName
{ get; set; }
public string XsdName
{ get: set; }
}
Function implementation:
public XmlFile fileName(string rate)
{
XmlFile file = new XmlFile();
if (rate == "2013")
{
file.XmlName = "abc";
file.XsdName = "def";
}
if (rate == "2014")
{
file.XmlName = "pqr";
file.XsdName = "xyz";
}
return file;
}
Please look at OO programming in detail, you will need it with C#.
Your question is vague, but I'll try my best.
Here's how to call fileName and receive the output for the last two parameters:
string xmlName;
string xsdName;
myInstance.fileName("2014", out xmlName, out xsdName);
I tend to shy away from using out. A better solution is to create a new class that wraps the data:
public class File
{
public File(string fileName, string xmlName, string xsdName)
{
FileName = fileName;
XmlName = xmlName;
XsdName = xsdName;
}
public string FileName
{
get;
private set;
}
public string XmlName
{
get;
private set;
}
public string XsdName
{
get;
private set;
}
}
public class OtherClass
{
public File FileName(string rate)
{
switch (rate)
{
case "2013":
return new File(..., "abc", "def");
case "2014":
return new File(..., "pqr", "xyz");
default:
throw new ArgumentException(String.Format("Unexpected rate '{0}'.", rate)); // Or, simply return null
}
}
}
To call a function with out parameters:
string xmlN;
string xsdN;
var result = fileName("rate value", out xmlN, out xsdN);
But one problem is that you need to assign them before the end of the function.
In this case, setting them to null might be appropriate.
There are several possible ways to do it. First is how you do it - output parameters
string xmlName, xsdName;
filename("xxx", out xmlName, out xsdName);
Second one is to use tuples.
public Tuple<string, string> fileName(string rate)
{
...
return Tuple.Create(xmlName, xsdName)
}
Third one is to define you own class
class XmlInfo
{
public string XmlName {get; set;}
public string XsdName {get; set;}
}
XmlInfo filename(string rate)
{
...
return new XmlInfo() { XmlName = xmlName, XsdName = xsdName };
}
Trying to get the result from a webservice call to return a Model. I eep getting the error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'CI.Models.Schedule' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
public Schedule getCourseSchedule()
{
var obj = new
{
States = new[] { new { State = "MX" } },
Zip = "",
Miles = "",
PaginationStart = 1,
PaginationLimit = 3
};
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "apoplication/json";
var url = "http://192.168.1.198:15014/ShoppingCart2/CourseSchedule";
var json = JsonConvert.SerializeObject(obj);
byte[] data = Encoding.UTF8.GetBytes(json);
byte[] result = client.UploadData(url, data);
string returnjson = Encoding.UTF8.GetString(result);
Schedule sched = JsonConvert.DeserializeObject<Schedule>(returnjson);
return sched;
}
}
Schedule Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Globalization;
namespace CI.Models
{
public class Schedule
{
public IEnumerable<Course> Courses { get; set; }
}
public class Course
{
/*
JSON Data returned from web service:
{
"ProgramGroup":"MR",
"ProgramCode":"RM",
"EventCode":"20160901MXMR",
"FormalDate":"September 1-2, 2016",
"StartDate":"2016\/09\/01",
"Price":5,
"LocName":"WB Hotel",
"LocAddress":"Av. Speedy Gonzales 220",
"LocCity":"Monterrey",
"LocState":"MX",
"LocZipCode":null,
"LicenseeURL":null,
"AgendaURL":"NA",
"SeatsAreAvailable":"2",
"GeneralInfoHTML":"General Info goes here.",
"GateKeeperHTML":null,
"EventType":"SS",
"TotalCourses":3
}
*/
public string ProgramGroup { get; set; }
public string ProgramCode { get; set; }
public string EventCode { get; set; }
public string FormalDate { get { return FormalDate; } set { FormalDate = convertFormalDateToSpanish(value); } }
public string StartDate { get; set; }
public double Price { get; set; }
public string LocName { get; set; }
public string LocAddress { get; set; }
public string LocCity { get ; set; }
public string LocState { get; set; }
public string LocZipCode { get; set; }
public string LicenseeURL { get; set; }
public string AgendaURL { get { return AgendaURL; } set { AgendaURL = buildAgendaLink(value); } }
public string SeatsAreAvailable { get; set; }
public string GeneralInfoHTML { get; set; }
public string GateKeeperHTML { get; set; }
public string EventType { get; set; }
public int TotalCourses { get; set; }
public string convertFormalDateToSpanish(string val)
{
DateTime TheDate = DateTime.Parse(StartDate);
string[] FormalDate = val.Split(" ".ToCharArray());
CultureInfo ci = new CultureInfo("es-ES");
string _Date = FormalDate[1].Replace("-", " al ").Replace(",", "");
string _Month = ci.TextInfo.ToTitleCase(TheDate.ToString("MMMM", ci));
val = string.Concat(_Date, " ", _Month);
return val;
}
private string buildAgendaLink(string val)
{
if (val.Trim() != "")
{
val = string.Concat("Agenda");
}
else
{
val = "Agenda";
}
return val;
}
}
}
Your server returns an array. Just try
Course[] courses = JsonConvert.DeserializeObject<Course[]>(returnjson);
Note that this is not an answer to your original problem, but I added it like an answer in order to explain my comment above with some actual code.
First problem with your code is that FormalDate and AgendaUrl properties simply won't work. Accessing them will result in a StackOverflowException, because you basically defined them recursively.
A property is merely syntax sugar for two separate getter/setter methods, so by writing this:
public class Course
{
public string FormalDate
{
get { return FormalDate; }
}
}
You are basically writing this:
public class Course
{
public string GetFormalDate()
{
// recursive call, with no terminating condition,
// will infinitely call itself until there is no
// more stack to store context data (and CLR
// will then throw an exception)
return GetFormalDate();
}
}
To fix that, you need to add an actual backing field, e.g.:
public class Course
{
private string _formalDate; // <-- this is a backing field;
// and this property uses the backing field to read/store data
public string FormalDate
{
get { return _formalDate; }
set { _formalDate = convertFormalDateToSpanish(value); }
}
}
Additionally, it's unusual for a property getter to return a different value than the one set through a setter. In other words, I would never expect this from a class:
var course = new Course();
course.StartDate = "2016/09/01";
course.FormalDate = "September 1-2, 2016";
Console.WriteLine(course.FormalDate); // prints "1 al 2 Septiembre" ?
I would rather move this functionality into a different class, or at least create different properties which return these values:
public class CourseInfo
{
// this is now a "dumb" auto-implemented property
// (no need for a backing field anymore)
public string FormalDate { get; set; }
// this read-only property returns the converted value
public string LocalizedFormalDate
{
get
{
return convertFormalDateToSpanish(FormalDate);
}
}
}
First of all I was unsure how to name this topic. I want to have a class with fields, and I would like to have some of them to have some Properties, Fields. Maybe I will show some of my code and explain it a bit.
class Column
{
public string Source { }
public string Destination { }
}
I want both "Source" and "Destination" to have inside them more 'fields'. For example "Name", "Type" etc.
I will read some text like:
Source home(1) type[3] Destination NewYork(3) Seattle[2]
I would like to be able to distinguish which part of my text to put inside "Source" and which inside "Destination". How should I do it?
I know that it may not be 100% clear, but I did my best to write it as simple as possible.
You need to define types and return those from those properties:
public class Column
{
private Source _Source = new Source();
private Destination _Destination = new Destination();
public Source Source { get { return _Source; } }
public Destination Destination { get { return _Destination; } }
}
public class Source
{
public string Name { get; set; }
public string Type { get; set; }
}
public class Destination
{
public string Name { get; set; }
public string Type { get; set; }
}
I want both "Source" and "Destination" to have inside them more 'fields'
You cannot do it like this as you have specified that they have type string.
If you feel that they should have some fields in them then consider making them objects like
public class Source
{
public string Name{get;set;}
public string Type{get;set;}
}
then you can edit your column class like this
public class Column
{
public Source Source { get;set;}
}
You can do the same for your other class as well
I know it's too late, but this is a full code that can help you, give it a try if you like.
class Column
{
string plainText;
Regex reg = new Regex(#"(Source\b\w+\b\w+\b)(Destination\b\w+\b\w+\b)");
public Source Source {
get {
Match m = reg.Match(plainText);
Group source = m.Groups[0];
string[] s = source.Value.Split(new string[]{" "}, StringSplitOptions.RemoveEmptyEntries);
return m.Success ? new Source(new string[]{s[1],s[2]}) : null;
}
set {
Match m = reg.Match(plainText);
Group dest = m.Groups[1];
if(m.Success) plainText = value + " " + dest.Value;
}
}
public Destination Destination {
get {
Match m = reg.Match(plainText);
Group dest = m.Groups[1];
string[] s = dest.Value.Split(new string[]{" "}, StringSplitOptions.RemoveEmptyEntries);
return m.Success ? new Destination(new string[]{s[1], s[2]}) : null;
}
set {
Match m = reg.Match(plainText);
Group source = m.Groups[0];
if(m.Success) plainText = source.Value + " " + value;
}
}
//This is used to change the plainText
//for example: SetPlainText("Source home(1) type[3] Destination NewYork(3) Seattle[2]");
public void SetPlainText(string txt){
plainText = txt;
}
}
public class Source
{
public Source(string[] s){
Name = s[0];
Type = s[1];
}
public string Name { get; set; }
public string Type { get; set; }
public override string ToString(){
return string.Format("Source {0} {1}",Name,Type);
}
}
public class Destination
{
public Destination(string[] s){
Name = s[0];
Type = s[1];
}
public string Name { get; set; }
public string Type { get; set; }
public override string ToString(){
return string.Format("Destination {0} {1}",Name,Type);
}
}