c# field with 'properties' - c#

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);
}
}

Related

Simplify passing nameof(someObject) and someObject as parameters to a method

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.

Assign string[] array field to another array field of same type c#

I have two classes defined in my solution
public class Registration {
[...]
public list<Account> Accounts {get; set;}
}
public class Account {
[...]
public string Code { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
In the web service that I am consuming, the following class definitions are available
public partial class VendReg {
[...]
private Payment_Details[] requestDetailsField;
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Payment_Details[] RequestDetails {
get {
return this.requestDetailsField;
}
set {
this.requestDetailsField = value;
}
}
}
public partial class Payment_Details {
private string bk_CodeField;
private string bk_NameField;
private string bk_AddressField;
public string Bk_Code {
get {
return this.bk_CodeField;
}
set {
this.bk_CodeField = value;
}
}
public string Bk_Name {
get {
return this.bk_NameField;
}
set {
this.bk_NameField = value;
}
}
public string Bk_Address {
get {
return this.bk_AddressField;
}
set {
this.bk_AddressField = value;
}
}
}
I want to assign Account to Request Details which is an array of Payment_Details. I tried this code below
vendReg.RequestDetails = registration.Accounts.Cast<Payment_Details>().ToArray();
I got invalid cast exception: Unable to cast object of type 'Account' to type 'Payment_Details'
Please guide on what I am not doing right
You need to convert this yourself (or you can look into things like Automapper)
vendReg.RequestDetails = registration.Accounts.Select(acc =>
new Payment_Details {
Bk_Code = acc.Code,
Bk_Name = acc.Name,
Bk_Address = acc.Address
}).ToArray();

C# Accessing custom attribute of owner object

How can I access the custom attribute of the parent or owner object.
Look at the FieldInfo property of the SQLFieldInfo struct
Here's a more detailed program that will compile and run that shows what I need.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Employee myclass = new Employee();
// Load from sql server...
myclass.Name = "Alain";
myclass.Age = 51;
//----
MessageBox.Show(myclass.Name.ToString()); // Should return Alain
MessageBox.Show(myclass.Age.FieldInfo.Type.ToString()); // Should output "int"
}
}
// This next class is generated by a helper exe that reads SQL table design and create the class from it
[SQLTableAttribute(DatabaseName = "Employees", Schema = "dbo", TableName = "Employees")]
public class Employee
{
[SQLFieldAttribute(FieldName = "ID", Type = SqlDbType.Int)]
public SQLFieldInfo<int> ID { get; set; }
[SQLFieldAttribute(FieldName = "Name", Type = SqlDbType.NVarChar, Size = 200)]
public SQLFieldInfo<String> Name { get; set; }
[SQLFieldAttribute(FieldName = "Age", Type = SqlDbType.Int)]
public SQLFieldInfo<int> Age { get; set; }
}
public struct SQLFieldInfo<T>
{
private readonly T value;
public SQLFieldInfo(T Value)
{
this.value = Value;
}
public static implicit operator SQLFieldInfo<T>(T Value)
{
return new SQLFieldInfo<T>(Value);
}
public T Value
{
get
{
return this.value;
}
}
public override string ToString()
{
return this.value.ToString();
}
public SQLFieldAttribute FieldInfo
{
get
{
// Need to retreive the attribute class of the parent or declaring member
return null;
}
}
}
// Holds the sql field information
public class SQLFieldAttribute : Attribute
{
public string FieldName { get; set; }
public SqlDbType Type { get; set; }
public bool AllowNull { get; set; }
public int Size { get; set; }
}
// Holds the sql table information
public class SQLTableAttribute : Attribute
{
public string DatabaseName { get; set; }
public string Schema { get; set; } = "dbo";
public string TableName { get; set; }
}
Thank you!
Alain
My data class is as follows (should be fairly translatable to A above):
public class Foo
{
[Argument(Help = "Name", AssignmentDelimiter = "=")]
public string Name
{
get;
set;
}
}
A helper class is responsible of reading attribute values of objects:
static public string GetCommandLineDelimiter<T>(Expression<Func<T>> property)
{
if(property != null)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
PropertyInfo prop = typeof(Arguments).GetProperty(propertyName);
if(prop != null)
{
object[] dbFieldAtts = prop.GetCustomAttributes(typeof(ArgumentAttribute), true);
if(dbFieldAtts.Length > 0)
{
return ((ArgumentAttribute)dbFieldAtts[0]).AssignmentDelimiter;
}
}
}
return null;
}
To use it, simply:
string delimiter = GetCommandLineDelimiter(() => myObject.Name);
That will get the attribute value of AssignmentDelimiter on property Name, i.e. "=".
First, MSDN is your friend.
Then, if you want to get the attributes for ancestors just specify true in the inherit flag of the method:
var attribute = typeof(A).GetProperty("myprop").GetCustomAttributes(true)
.OfType<MycustomAttrib>().FirstOrDefault();
This works. I am doing a lazy initialization of a reference to the custom attribute by using reflection to look at all the properties of all the types.
public class MycustomAttribAttribute : Attribute
{
public MycustomAttribAttribute(string name)
{
this.Name=name;
}
public string Name { get; private set; }
}
class A
{
public A() { MyProp=new B(); }
[MycustomAttrib(name: "OK")]
public B MyProp { get; set; }
}
class B
{
private static Lazy<MycustomAttribAttribute> att = new Lazy<MycustomAttribAttribute>(() =>
{
var types = System.Reflection.Assembly.GetExecutingAssembly().DefinedTypes;
foreach(var item in types)
{
foreach(var prop in item.DeclaredProperties)
{
var attr = prop.GetCustomAttributes(typeof(MycustomAttribAttribute), false);
if(attr.Length>0)
{
return attr[0] as MycustomAttribAttribute;
}
}
}
return null;
});
public string MyProp2
{
get
{
return att.Value.Name;
}
}
}
class Program
{
static void Main(string[] args)
{
// Finds the attribute reference and returns "OK"
string name = (new A()).MyProp.MyProp2;
// Uses the stored attribute reference to return "OK"
string name2 = (new A()).MyProp.MyProp2;
}
}

Automatically determine MaxLength and use it for padding

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)

Parsing JSON into Object

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);
}
}
}

Categories