Want to access a class property(field) directly from the object
Want to access a class property(field) directly from the object
I've written a class that I want to use as a global data interface for interacting with data coming in from a web-based API. As such, the class is able to take in a value of virtually any base-type and work as if it was that base-type.
Under the hood I store the "value" of the object as a string, and, for the most part, the class acts as a clone of the String object, except that it can try to impersonate any other base class in-context.
This is all kind of superfluous though, my problem / question is, how can I make it so that fields of this type are able to interact with it directly instead of through an Accessor?
For Example:
public class PolyVar
protected string _myValue;
public PolyVar() this._myValue = "";
public PolyVar(string value) this._myValue = value;
public string Value get => this._myValue; set => this._myValue = value;
Then, somewhere in the project I want do this:
string temp = "";
PolyVar work = ""; // in lieu of "PolyVar work = new PolyVar();"
temp = "Some string data here. " + work; // using just "work" instead of "work.Value"
So can you construct the class in such a way that accessing/modifying
one of its properties (in this case, "_myValue") directly through the class itself is possible in lieu of necessitating the use of an accessor? (as the base classes already all support?)
I'm afraid that's not possible, at least not with the way you currently want it.
– Haytam
2 days ago
Are you looking for the implicit operator? With that, you can define that other data types (like
string
) can be implicitly converted to your data type, and also the other way around.– Corak
2 days ago
string
+Ondrej Tucny -- I have a base encapsulating class that receives unformatted data from a web api. When the data arrives, this class has no idea what data-types each of the fields are, but whenever the data needs to be referenced elsewhere in the application, this encapsulator has to be able to facilitate managing the data according to whatever the downstream needs necessitate, REGARDLESS of what the specific individual types are that are being held (the data comes in via XML whenever a request is made to the API, and can contain anything that a particular call requests from the DB).
– Brett Leuszler
2 days ago
Btw. this sounds dangerously close to stingly typed data, which is very problematic (culture specific stuff alone is a big enough argument against it). And there is already a "catch-all" type:
object
(with all the same problems of non-type-safety that your solution seems to have). I'd strongly recommend against using this approach! If at all possible, I'd suggest using generics– Corak
2 days ago
object
4 Answers
4
You have to 1) create an implicit conversion from string to PolyVar, and 2) override ToString()
so it is converted properly to a simple string when needed.
ToString()
public class PolyVar
protected string _myValue;
public PolyVar() this._myValue = "";
public PolyVar(string value) this._myValue = value;
public string Value get return this._myValue; set this._myValue = value;
// Override ToString() so "" + polyVar1 is correctly converted.
public override string ToString()
return this.Value;
// Create an implicit cast operator from string to PolyVar
public static implicit operator PolyVar(string x)
return new PolyVar( x );
class Ppal
static void Main()
PolyVar work = "data"; // in lieu of "PolyVar work = new PolyVar();"
string temp = "Some string data here: " + work;
System.Console.WriteLine( temp );
Hope this helps.
Hmm, I see what you're doing there, but, for what it's worth, I'm still getting "Cannot convert from 'PolyVar' to 'String'" errors whenever I try to dereference the class without a property (i.e. ".Value" or ".ToString()"....
– Brett Leuszler
2 days ago
@BrettLeuszler -- you can also have
public static implicit operator string(PolyVar x) return x.Value;
– Corak
2 days ago
public static implicit operator string(PolyVar x) return x.Value;
Perhaps another implicit operator the other way around (to string). Otherwise
string str = work;
doesn't compile. Although part of me feels it should be explicit instead, implicit seems to match what the OP wants– pinkfloydx33
2 days ago
string str = work;
Ah hah! -- that (plus adding a couple of operator overrides for managing string concatenation) seems to have sorted it out!
– Brett Leuszler
2 days ago
Modify your PolyVar
class so that it overrides the ToString()
method:
PolyVar
ToString()
public class PolyVar
protected string _myValue;
public PolyVar() this._myValue = "";
public PolyVar(string value) this._myValue = value;
public string Value get => this._myValue; set => this._myValue = value;
public override string ToString()
return this.Value.ToString();
That allows you to do the following ...
PolyVar work = new PolyVar();
string temp = "Some string data here. " + work.ToString();
... or as Corak mentioned below, just leave out .ToString()
as it gets called automatically ...
.ToString()
string temp = "Some string data here. " + work;
As your explanation states that you are using multiple objects of PolyVar
I don't really see a way which allows you to skip using the constructor.
PolyVar
... and "adding" the instance to a string automatically calls
.ToString()
. So you can actually write: string temp = "Some string data here. " + work;
– Corak
2 days ago
.ToString()
string temp = "Some string data here. " + work;
I don't mind having to use the constructor, it's the inline application where I didn't want to have to always use the ".Value" accessor virtually every time I wanted to reference the class.
– Brett Leuszler
2 days ago
You don't need to use string
here, you can go with generics:
string
public class PolyVar<T>
public PolyVar()
public PolyVar(T value)
Value = value;
// note: no reason to explicitly declare the backing field
public T Value get; set;
// return the string representation of the current value
public override string ToString() => Value.ToString();
// allow any type to be implicitly a PolyVar
public static implicit operator PolyVar<T>(T value) => new PolyVar<T>(value);
You would then use this as:
PolyVar<string> work = "";
string data = "Some data: " + work;
This would allow you to use any type without having to parse from and convert back to string.
As mentioned by @Corak, if you don't know T
before-hand, you can use a Factory approach for creating PolyVar<T>
s :
T
PolyVar<T>
public static class PolyVarFactory
public static PolyVar<T> FromValue<T>(T value) => value;
Notice that this has to be in a non-generic class for you to be able to call the method without explicitly passing-in a type.
And with a little factory method like
PolyVar<T> CreatePolyVar<T>(T value) return new PolyVar<T>(value);
you wouldn't even have to know what to write for T
. Simply say var work = CreatePolyVar(whateverValue);
– Corak
2 days ago
PolyVar<T> CreatePolyVar<T>(T value) return new PolyVar<T>(value);
T
var work = CreatePolyVar(whateverValue);
@Corak Nice addition, thanks!
– Camilo Terevinto
2 days ago
Except that it's not impossible that I'll refer to the SAME record multiple times in different ways. The PolyVar class I posted below can, without any modification or re-reference, be treated inline as an Int, a Float, a String or any one of the other base types it's been built to understand. Plus built on top of all that, the overridden operators allow it to be seemlessly added, subtracted, multiplied or divided by/with any of the numeric basetypes seamlessly...
– Brett Leuszler
2 days ago
@BrettLeuszler It is also an extremely inefficient thousand-lines-long class
– Camilo Terevinto
2 days ago
Well, I've trimmed it down somewhat (got rid of all constructors that amounted to "public PolyVar(type var) this._value=type.ToString(); " and letting the generic "object" constructor do the work instead. Still, implementing full overloading for ten or more operators across a dozen plus incorporating bi-directional conversions for all of those types, is just going to take a lot of lines. Though I'm MORE than happy to implement any shortcuts or suggestions on how to reduce that without losing functionality! (also another ~100 lines are comments and support for IEnumerator...)
– Brett Leuszler
2 days ago
Okay, so, in the event that anyone else wanted to do something like this, here's what I ultimately ended up with:
It basically acts like a string, but it also facilitates direct addition, multiplication, subtraction and division with all base numeric types, numeric quantity evaluation (>, <, ==, <= and >=) and the ability to seemlessly act as one of the supported subtypes through appropriately named accessors (i.e. ".AsInt", "AsFloat", "AsDecimal", "AsULong" etc.). It also incorporates the most common String functions (Substring, PadLeft, PadRight) as well as some extensions I use frequently (".UCWords" and ".Filter"). Adding more string functions is obviously pretty easy. It supports char-based ForEach enumeration, as well as index-based character access / assignment. Substring has also been extended to support negative "start position" values (indicating "start x places back from the end"). I'm posting it in case such a class is ever useful for anyone else, either for what it is/does, or how I addressed the problem posed with the bits of help supplied here. Oh, and although I went back and forth on it, I also implemented local error capture whenever a conversion is attempted. So a value is always returned by calls to alternate types. You can check the ".HasError" (and ".Error") accessors afterwards to see if there were problems. Obviously this may not be the preferred method for dealing with conversion errors in all (or even most) circumstances, but it's how I went with it for mine. Suggestions, and comments welcomed.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace PolyVar
class PolyVar : IEnumerator<char>
protected string _value = "";
protected string _error = "";
protected int _position = 0;
#region Constructors
public PolyVar()
public PolyVar(string value) => this._value = value;
public PolyVar(XmlDocument data) => this._value = data.OuterXml;
public PolyVar(XmlElement data) => this._value = data.OuterXml;
public PolyVar(bool value) => this.AsBool = value;
public PolyVar(PolyVar data) => this._value = data._value;
public PolyVar(object data) => this._value = data.ToString();
#endregion
#region Operators
public static PolyVar operator +(PolyVar left, string right) => new PolyVar(left._value + right);
public static PolyVar operator +(string left, PolyVar right) => new PolyVar(left + right._value);
public static PolyVar operator +(PolyVar left, PolyVar right) => new PolyVar(left._value + right._value);
public static bool operator ==(string left, PolyVar right) => (right == left);
public static bool operator ==(PolyVar left, string right)
if (left is null) return (right is null)
public static bool operator !=(PolyVar left, string right) => !(left == right);
public static bool operator !=(string left, PolyVar right) => !(right == left);
public static bool operator ==(PolyVar left, PolyVar right)
if (left is null) return (right is null)
public static bool operator !=(PolyVar left, PolyVar right) => !(left == right);
public static bool operator ==(bool left, PolyVar right) => right == left;
public static bool operator ==(PolyVar left, bool right)
if (left is null) return false;
return left.AsBool == right;
public static bool operator !=(PolyVar left, bool right) => !(left == right);
public static bool operator !=(bool left, PolyVar right) => !(right == left);
public static bool operator ==(XmlDocument left, PolyVar right) => (right == left);
public static bool operator ==(PolyVar left, XmlDocument right)
if (left is null) return (right is null);
if (right is null) return false;
return String.Equals(left._value, right.OuterXml, StringComparison.InvariantCultureIgnoreCase);
public static bool operator !=(PolyVar left, XmlDocument right) => !(left == right);
public static bool operator !=(XmlDocument left, PolyVar right) => !(right == left);
public static bool operator ==(XmlElement left, PolyVar right) => (right == left);
public static bool operator ==(PolyVar left, XmlElement right)
if (left is null) return (right is null);
if (right is null) return false;
return String.Equals(left._value, right.OuterXml, StringComparison.InvariantCultureIgnoreCase);
public static bool operator !=(PolyVar left, XmlElement right) => !(left == right);
public static bool operator !=(XmlElement left, PolyVar right) => !(right == left);
#endregion
#region Accessors
public string Value get => this._value; set => this._value = value;
public int Length
get => _value.Length;
set if (value < _value.Length) this._value = this._value.Substring(0, value);
public string Words => ((this._value.Trim().IndexOf(' ') > 0) ? this._value.Trim().Split(new string " ", "r", "n", "t" , StringSplitOptions.RemoveEmptyEntries) : new string this._value.Trim() );
protected string FirstWord => this.Words[0];
public char this[int index]
get
(index >= _value.Length))
throw new ArgumentOutOfRangeException();
return this._value[index];
set
(index >= _value.Length))
throw new ArgumentOutOfRangeException();
string work = this._value.Substring(0, index) + value;
if (index < _value.Length - 1)
work += this._value.Substring(index + 1);
this._value = work;
public bool HasError => (this._error.Length > 0);
public string Error get string e = this._error; this._error = ""; return e;
protected bool AsBool
get
((int)this != 0);
set => this._value = (value ? "true" : "false");
public XmlDocument AsXmlDocument
set => this._value = value.OuterXml;
get
XmlDocument doc = new XmlDocument();
this._error = "";
try doc.LoadXml(this._value);
catch (Exception e) this._error = e.Message;
return doc;
public XmlElement AsXmlElement
set => this._value = value.OuterXml;
get
XmlDocument doc = new XmlDocument();
this._error = "";
XmlElement result = null;
try
doc.LoadXml(AsXmlDocument.CreateXmlDeclaration("1.1", "UTF-8", "yes").ToString() + this._value);
result = (XmlElement)doc.FirstChild;
catch (Exception e)
this._error = e.Message;
result = doc.CreateElement("error");
result.InnerText = e.Message;
return result;
protected object Set set => this._value = value.ToString();
char IEnumerator<char>.Current => this._value[this._position];
object IEnumerator.Current => this._value[this._position];
#endregion
#region Methods
// Generic number conversion function: Attempts to convert the internal _myValue
// string into the numeric type specified by the "T" designator:
protected T ConvertToNbr<T>()
try return (T)Convert.ChangeType(this._value, typeof(T));
catch (Exception e)
this._error = e.Message;
return default(T);
public string Substring(int start, int length) =>
(start >= 0) ? this._value.Substring(start, length) : this._value.Substring(_value.Length + start, length);
public string Substring(int start) =>
(start >= 0) ? this._value.Substring(start) : this._value.Substring(_value.Length + start);
public string PadLeft(int toWidth, char with) => this._value.PadLeft(toWidth, with);
public string PadLeft(int toWidth) => this._value.PadLeft(toWidth);
public string PadRight(int toWidth, char with) => this._value.PadRight(toWidth, with);
public string PadRight(int toWidth) => this._value.PadRight(toWidth);
public char ToCharArray() => this._value.ToCharArray();
public char ToCharArray(int startIndex, int Length) => this._value.ToCharArray(startIndex, Length);
/// <summary>Removes all instances of a specified character from the string.</summary>
/// <param name="value">A Char value to remove all instances of from this string.</param>
/// <returns>The current string with all of the specified characters removed.</returns>
public string Remove(char value) => this._value.Replace(value.ToString(), "");
/// <summary>Removes all instances of a specified string from the string.</summary>
/// <param name="value">A string value to remove all instances of from this string.</param>
/// <returns>The current string with all of the specified string removed.</returns>
public string Remove(string value) => this._value.Replace(value, "");
/// <summary>Removes all instances of each element in a specified character array from the string.</summary>
/// <param name="value">An array of Char value to remove all instances of from this string.</param>
/// <returns>The current string with all of the specified characters removed.</returns>
public string Remove(char values)
string result = this._value;
foreach (char c in values) result = result.Remove(c);
return result;
/// <summary>Extends the string class to add a UCWords function.</summary>
/// <returns>A string with the initial letter of all words in it capitalised with any existing capitalized letters left as found.</returns>
public string UCWords()
System.Globalization.TextInfo ti = System.Globalization.CultureInfo.InvariantCulture.TextInfo;
return ti.ToTitleCase(this._value.ToLowerInvariant());
/// <summary>Extends the string class to add a UCWords function.</summary>
/// <param name="strict">If set to true, all letters in the string are converted to lowercase, then the words are capitalised.</param>
/// <returns>A string with all individual words in it capitalised.</returns>
public string UCWords(bool strict)
("> .trn".IndexOf(result[i - 1]) > 0)) && (">abcdefghijklmnopqrstuvwxyz".IndexOf(result[i]) > 0))
result[i] = char.ToUpperInvariant(result[i]);
return result;
/// <summary>Given a string of valid characters, filters all non-matching characters out of a string.</summary>
/// <param name="validChars">A string of valid (permitted) characters to retain.</param>
/// <param name="ignoreCase">Specifies whether case should be ignored.</param>
/// <returns>A string containing only the permitted characters.</returns>
public string Filter(string validChars, bool ignoreCase)
string result = this._value;
if ((result.Length == 0)
/// <summary>Given a string of valid characters, filters all non-matching (case-insensitive) characters out of a string.</summary>
/// <param name="validChars">A string of valid (permitted) characters to retain.</param>
/// <returns>A string containing only the permitted characters.</returns>
public string Filter(string validChars) => this.Filter(validChars, true);
/// <summary>Given an array valid characters, filters all non-matching (case-insensitive) characters out of a string.</summary>
/// <param name="validChars">An array of valid (permitted) characters to retain.</param>
/// <returns>A string containing only the permitted characters.</returns>
public string Filter(char validChars) => this.Filter(new string(validChars), true);
/// <summary>Given an array valid characters, filters all non-matching characters out of a string.</summary>
/// <param name="validChars">An array of valid (permitted) characters to retain.</param>
/// <param name="ignoreCase">Specifies whether case should be ignored.</param>
/// <returns>A string containing only the permitted characters.</returns>
public string Filter(char validChars, bool ignoreCase) => this.Filter(new string(validChars), ignoreCase);
public static implicit operator string(PolyVar data) => data.Value;
public static implicit operator PolyVar(string data) => new PolyVar(data);
public static implicit operator int(PolyVar data) => data.ConvertToNbr<int>();
public static implicit operator PolVar(int data) => new PolyVar(data);
public static implicit operator sbyte(PolyVar data) => data.ConvertToNbr<sbyte>();
public static implicit operator PolyVar(sbyte data) => new PolyVar(data);
public static implicit operator short(PolyVar data) => data.ConvertToNbr<short>();
public static implicit operator PolyVar(short data) => new PolyVar(data);
public static implicit operator long(PolyVar data) => data.ConvertToNbr<long>();
public static implicit operator PolyVar(long data) => new PolyVar(data);
public static implicit operator decimal(PolyVar data) => data.ConvertToNbr<decimal>();
public static implicit operator PolyVar(decimal data) => new PolyVar(data);
public static implicit operator float(PolyVar data) => data.ConvertToNbr<float>();
public static implicit operator PolyVar(float data) => new PolyVar(data);
public static implicit operator double(PolyVar data) => data.ConvertToNbr<double>();
public static implicit operator PolyVar(double data) => new PolyVar(data);
public static implicit operator uint(PolyVar data) => data.ConvertToNbr<uint>();
public static implicit operator PolyVar(uint data) => new PolyVar(data);
public static implicit operator byte(PolyVar data) => data.ConvertToNbr<byte>();
public static implicit operator PolyVar(byte data) => new PolyVar(data);
public static implicit operator ulong(PolyVar data) => data.ConvertToNbr<ulong>();
public static implicit operator PolyVar(ulong data) => new PolyVar(data);
public static implicit operator ushort(PolyVar data) => data.ConvertToNbr<ushort>();
public static implicit operator PolyVar(ushort data) => new PolyVar(data);
public static implicit operator bool(PolyVar data) => data.AsBool;
public static implicit operator PolyVar(bool data) => new PolyVar(data);
public static implicit operator XmlDocument(PolyVar data) => data.AsXmlDocument;
public static implicit operator PolyVar(XmlDocument data) => new PolyVar(data);
public static implicit operator XmlElement(PolyVar data) => data.AsXmlElement;
public static implicit operator PolyVar(XmlElement data) => new PolyVar(data);
public override string ToString() => this._value;
public override bool Equals(object obj) => this._value.Equals(obj);
public override int GetHashCode() => this._value.GetHashCode();
public IEnumerator<char> GetEnumerator() => this._value.GetEnumerator();
bool IEnumerator.MoveNext() => (++this._position) < this._value.Length;
void IEnumerator.Reset() => this._position = 0;
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
if (!disposedValue)
if (disposing)
// TODO: dispose managed state (managed objects).
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~AppletParameters()
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
//
// This code added to correctly implement the disposable pattern.
void IDisposable.Dispose()
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
#endregion
#endregion
Interesting class. Q: what's "ArgSwitch" ? Type not found. Also around line 589 there is a issue with calling UCWords on the string type.. and on line 681, the compiler reports a problem with result.Remove(result.Substring(i, 1)) which should be result.RemoveAt(i) ?
– Goodies
2 days ago
ArgSwitch is another class that I was adding functionality with for my implementation, but isn't relevant to a generic application of this class, I think I took out the references to it that I'd accidently left in. The reference for String.Remove is a string extension I've been using so long, I forgot it wasn't native!
public static string Remove(this string source, char value) => source.Replace(value.ToString(), "");
– Brett Leuszler
2 days ago
public static string Remove(this string source, char value) => source.Replace(value.ToString(), "");
In the interest of cutting down the length, I replaced all of the individual numeric type conversions in the related Accessors with a call to the new
Convert(Type t)
function that uses reflection and dynamic typing to do on the fly conversion of a string into any numeric class with just a single function.– Brett Leuszler
2 days ago
Convert(Type t)
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Wondering why you want such value storage class.
– Ondrej Tucny
2 days ago