I want to replace the struct in the following code with a parent class with no data members and four subclasses, each subclass adds a different field. e.g. The SMValueFlt subclass adds a field named fltValue, and so on.
I am very new to C# and my Java is very rusty, so this is proving harder than I thought. And beyond actually setting up the class and subclasses i'm not sure how to proceed. Any help would be appreciated.
public class Interpreter {
enum TypeCode { None, IntType, FloatType, StringType };
struct SMValue {
public TypeCode t;
public int intValue;
public float fltValue;
public string strValue;
public SMValue( int i ) {
t = TypeCode.IntType; intValue = i; fltValue = 0.0F; strValue = null; }
public SMValue( float f ) {
t = TypeCode.FloatType; fltValue = f; intValue = 0; strValue = null; }
public SMValue( string s ) {
t = TypeCode.StringType; strValue = s; intValue = 0; fltValue = 0.0F; }
public override string ToString() {
if (t == TypeCode.IntType) return String.Format("{0}", intValue);
if (t == TypeCode.FloatType) return String.Format("{0}", fltValue);
if (t == TypeCode.StringType)
return strValue==null? "--null--" : strValue;
return "???";
}
}
}
I kept your TypeCode around in the first example, but it's not really necessary. You can inspect the type of a variable at runtime. For example,
var x = new SMFltValue() // (x.GetType() == typeof(SMFltValue)) = true, x is SMFltValue = true
Without using generics:
public enum TypeCode { IntType, FloatType, StringType };
public abstract class SMValue {
public TypeCode t;
public SMValue(TypeCode typeCode) {
t = typeCode;
}
public abstract string ToString();
}
public class SMFltValue : SMValue {
public float fltValue;
public SMFltValue(float f) : base(TypeCode.FloatType)
{
fltValue = f;
}
public override string ToString()
{
return String.Format("{0}", fltValue);
return String.Format("{0}", intValue);
return strValue==null ? "--null--" : strValue;
}
}
public class SMIntValue : SMValue {
public int intValue;
public SMIntValue(int i) : base(TypeCode.IntType)
{
intValue = i;
}
public override string ToString()
{
return String.Format("{0}", intValue);
}
}
public class SMStrValue : SMValue {
public string strValue;
public SMStrValue(string s) : base(TypeCode.StringType)
{
strValue = s;
}
public override string ToString()
{
return strValue==null ? "--null--" : strValue;
}
}
But generics would make it much nicer.
public class SMValue<T> {
public T value;
public SMValue(T value) {
this.value = value;
}
public string ToString() {
if (value == null)
{
return "--null--";
}
else
{
return string.Format("{0}", value);
}
}
}
Then you could use it as.
int i = 3;
float f = 5.0f;
string s = null;
new SMValue<int>(i).ToString() ==> 3
new SMValue<float>(f).ToString() ==> 5.0
new SMValue<string>(s).ToString() ==> "--null--"
The <int>, <float>, <string> aren't actually necessary because the compiler can infer the type from the variable being passed to the constructor.
The semantics of a struct with exposed fields are fundamentally different from those of a class. Fundamentally, each structure-type variable holds a bunch of fields stuck together with duct tape, while a class-type variable holds a not-necessarily-unique reference to a class object. If a structure type has two int fields, and one has two variables of that type, one has four integers which may be written independently. By contrast, if a class type has two int fields and one has two variables of that type, it's possible that the variables may at any given time reference different instances (in which case they would encapsulate a total of four independently-writable integers), or they may identify the same instance (in which case both variables would identify the same pair of integers, and so writing the first number in one pair would also write the first number in the other).
Some people think all types should behave like class objects, and regard as "evil" any types that don't. In reality, there are situations where it's useful to stick a bunch of variables together with duct tape (so they may be passed around as a unit when convenient), but guarantee that every bunch of variables is distinct. Class types can be used to mimic this behavior, awkwardly, but structures naturally work that way.
Without knowing exactly how you intend to use your type, it's hard to say whether a class will be able to fulfill your needs without having to rework all your client code. It's important to note, however, that any class used to replace a struct must almost always be immutable. If you can't easily convert your struct to a mutable class, you'll probably have to keep it a struct.
Related
Note: This question is nearly identical to this one. But this one is about C#, not Java.
In Ada, it is possible to create incompatible equivalent numeric types:
type Integer_1 is range 1 .. 10;
type Integer_2 is range 1 .. 10;
A : Integer_1 := 8;
B : Integer_2 := A; -- illegal!
This prevents accidental logical errors such as adding a temperature to a distance.
Is it possible to do something similar in C#? E.g.
class BookId : int {}
class Book
{
BookId Id;
}
class PageId : int {}
class Page
{
PageId Id;
}
class Word
{
BookId BookId;
PageId PageId;
string text;
}
var book = new Book { Id = 1 };
var page = new Page { Id = 1 };
var book = new Word
{
BookId = book.Id, // Ok
PageId = book.Id, // Illegal!
string = "eratosthenes"
};
Yes, you can create types that behave like numeric values but can't be assigned to each other. You can't derive from numeric type, but wrapping one into a struct would be comparable efficient (if that's a concern) or you can add more info (like units). You may be even create generic type if you don't need cross-type operations.
You can see Complex type for full set of operations and interfaces that make type behave very close to regular numbers (including plenty of conversions back and forth as needed).
Some basic class:
class Distance
{
float d;
public Distance(float d)
{
this.d = d;
}
public static Distance operator+(Distance op1, Distance op2)
{
return new Distance(op1.d + op2.d);
}
// ==, !=, Equals and GetHashCode are not required but if you
// need one (i.e. for comparison you need ==, to use values of this
// type in Dictionaries you need GetHashCode)
// you have to implement all
public static bool operator == (Distance op1, Distance op2)
{
return op1.d == op2.d;
}
public static bool operator !=(Distance op1, Distance op2)
{
return op1.d != op2.d;
}
public override bool Equals(object obj)
{
return (object)this == obj || ((obj is Distance) && (obj as Distance)==this);
}
public override int GetHashCode()
{
return d.GetHashCode();
}
// Some basic ToString so we can print it in Console/use in
// String.Format calls
public override string ToString()
{
return $"{d} M";
}
}
Which lets you add values of the same type but will fail to add any other type:
Console.WriteLine(new Distance(1) + new Distance(2)); // "3 M"
// Console.WriteLine(new Distance(1) + 2); // fails to compile
Picking between class and struct for such sample is mostly personal preference, for real usage make sure to know difference between value and reference type before picking one and decide what works for you (struct is likely better for numbers).
More information:
Units of measure in C# - almost - even if you don't go all the way it shows how to make generic numeric type so you can easily create many types without much code (UnitDouble<T> in that post), Arithmetic operator overloading for a generic class in C# - discusses issues you face if you want to go other way and support varying base numeric types (like Distance<float> and Distance<int>).
It turns out my needs were a bit more simple than I thought. What I needed was a unique ID that could not be confused with another unique ID. In the end, I went with a template wrapper for an int.
class Id<T> {
private int id;
public Id(int id) { this.id = id; }
public static implicit operator ID<T>(int value) { return new ID<T>(value); }
public static implicit operator int(ID<T> value) { return value?.id ?? 0; }
public static implicit operator int?(ID<T> value) { return value?.id; }
public static implicit operator ID<T>(int? value)
{
if (value == null) { return null; }
return new ID<T>(value.Value);
}
public override string ToString() { return id.ToString(); }
}
class Book { Id<Book> Id; }
class Page { Id<Page> Id; }
Book.Id cannot be assigned to Page.Id, but either can go back and forth with ints.
I realise now that I've seen this pattern before somewhere, so I guess it's not that original...
Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890
I have enums like the following in my DB:
"Random Type", "Random Type1", "NewRandom"
Normally, I would represent the values in an enum like:
enum myTypes
{
Random Type = 0,...
}
but this is not possible, so I tried using a class
static class myTypes
{
public const string RandomType = "Random Type";
public const string NewRandom = "NewRandom";
}
This way, I can use the class like an Enum, but I'm wondering if this is the best implementation possible? Or is there away around creating Enums to allow space?
Thanks.
EDIT:
Please, I would also love to know whether there is anything wrong with my current implementation. I have a feeling my current implementation is better than most suggested solutions here.
Thanks
No, you can't do that. Enums are just typesafe ints.
There is a solution available, and I quite like it. Use the DescriptionAttribute.
You'd use it like this:
static enum myTypes
{
[Description("Random Type")]
RandomType,
[Descripton("New Random")]
NewRandom
}
and then you'd also need this extension method:
public static string GetDescription<T>(this T en) where T : struct, IConvertible
{
Type type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("The type is not an enum");
}
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return en.ToString();
}
And then with that, you could just do this:
myTypes.RandomType.GetDescription();
Enums are much similar to numbers (Integers specifically), rather than strings or so. Adhering to numbered Enums yields you to easy casting, flags-composition (e.g. AND, OR, etc).
I wouldn't use string constant in place of Enums, unless that will bring you more benefits than penalties.
If your goal is to describe to the user the Enum options, I'd suggest to consider to enrich each item with a Description attribute. It's a metadata, rather a real data, but it's also pretty easy to read using reflection.
Cheers
What I do is I define custom attribute [DisplayName(string)] that can be attached to enum values. You define your enum with display name on the values you wish were named with spaces / special characters:
public enum Test
{
None = 0,
[DisplayName("My Value")]
MyValue = 1,
[DisplayName("Spęćiał")]
Special = 2
}
Your implementation in addition to getting enum value name should also check if DisplayName attribute is set, and if so, it should take display name instead.
I would go with display name attributes:
[AttributeUsage(AttributeTargets.Field)]
public class EnumDisplayNameAttribute : DisplayNameAttribute
{
public EnumDisplayNameAttribute()
: base(string.Empty)
{
}
public EnumDisplayNameAttribute(string displayName)
: base(displayName)
{
}
}
public static class EnumExtensions
{
public static string ToDisplayName(this Enum enumValue)
{
var builder = new StringBuilder();
var fields = GetEnumFields(enumValue);
if (fields[0] != null)
for (int i = 0; i < fields.Length; i++)
{
var value = fields[i]
.GetCustomAttributes(typeof(EnumDisplayNameAttribute), false)
.OfType<EnumDisplayNameAttribute>()
.FirstOrDefault();
builder.Append(value != null
? value.DisplayName
: enumValue.ToString());
if (i != fields.Length - 1)
builder.Append(", ");
}
return builder.ToString();
}
private static FieldInfo[] GetEnumFields(Enum enumValue)
{
var type = enumValue.GetType();
return enumValue
.ToString()
.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(type.GetField)
.ToArray();
}
}
usage for type:
public enum MyType
{
[DisplayName("Random Type")]
RandomType,
[DisplayName("New Random")]
NewRandom
}
would be:
var enumVariable = MyType.RandomType;
var stringRepresentation = enumVariable.ToDisplayName();
note that with that approach you would get ToString values if you omit attribute for some enum members.
You should probably not use strings as type indicators in your database. Use integers instead. If you like, you can have a "type table" in your database, where you can store the type names, instead of repeating them through the tables that uses them.
If you do this, then you can convert the integers from the database to enums as suggested above.
You can use Typesafe Enum pattern to achieve your goal.
Idea is to wrap your enum around a class. I guess this is what you want -
public class MyTypes
{
#region Enum Values
public static MyTypes RandomType = new MyTypes(0, "Random Type");
public static MyTypes NewRandom = new MyTypes(1, "New Random");
#endregion
#region Private members
private int id;
private string value;
private MyTypes(int id, string value)
{
this.id = id;
this.value = value;
}
#endregion
#region Overriden members
public override string ToString()
{
return value;
}
#endregion
public static List<MyTypes> GetValues()
{
return new List<MyTypes>() { MyTypes.RandomType, MyTypes.NewRandom };
}
}
I discovered yesterday that I can't have a Class that uses a field named "Type" this is probably reserved.
Although the field may be reserved you still can't set anyObject.Type if it doesn't have a Type field defined as a public string. Ignoring any getters and setters and jumping directly to changing YourObject to "whatever" string.
Try this for yourself. Define a Type field and try setting it.
This should be reported to Microsoft so no one will use "Type" as a field in the future, there is no warnings/errors along trying to define it.
public Point_Extended(Point p, booking b)
{
this.ID = p.ID;
this.Type = p.Type;
this.Status = p.Status;
this.Size = p.Size;
this.Coords = p.Coords;
this.Color = p.Color;
this.BuildingID = p.BuildingID;
this.Login = b.Login;
this.Starts = b.Starts;
this.Hours = b.Hours;
this.BookingID = b.ID;
}
If there is a name abiguity - just use this.Type / obj.Type (instance), TypeName.Type (static) or System.Type (the type). Or in really nasty cases, global::System.Type. This works just fine and matches the question (I think):
static class Program
{
static void Main() {
Test anyObject = new Test();
anyObject.Type = "abc";
}
}
class Test
{
public string Type;
}
You are defining "Type" in a scope local to your class, e.g.
class SomeClass
{
public string Type { get; set; }
}
and then using it in some method of that class, e.g.
class SomeClass
{
public string Type { get; set; }
public void DoSomeStuff()
{
Type = "Foo";
}
}
This is ambiguous between "Type" in SomeClass (a property) and "Type" in namespace System (a type).
Nevermind.
It was the ToString() override representation that confused me to think the Type was changed.
Let's say I have a data object, but this object can hold one of several types of data.
class Foo
{
int intFoo;
double doubleFoo;
string stringFoo;
}
Now, I want to create an accessor. Some way to get at this data. Obviously, I could create multiple accessors:
public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();
Or I could create multiple properties
public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }
I don't that this is a very good design. It requires the client code to be more concerned about type than it should have to be. What's more, I really need only a single value for this class and the above would allow one of each type to be assigned at the same time. Not good.
One option is to use Generics.
class Foo<T>
{
public T TheFoo { get; set; }
}
However, this doesn't create a Foo, it creates a Foo<T>. A different type for each, so I can't really use them as the same type.
I could derive Foo<T> from FooBase, then treat all of them as FooBase's, but then i'm back in the problem of accessing the data.
A different Generics option is to use something like this:
class Foo
{
string stringRepresentationOfFoo;
public T GetFoo<T>() { return /* code to convert string to type */ }
}
OF course the problem is that any kind of T could be passed, and frankly, it's a bit busy.
I could also just box the values and return an object, but then there is no type safety.
Ideally, I want to treat all Foo's the same, but I want type safety so that if there isn't a StringFoo, I can't even compile a reference to a StringFoo.
Foo foo = new Foo("Foo");
string sFoo = foo.Value; // succeeds.
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error
Perhaps this isn't even possible.. and I'll have to make some compromises, but maybe i'm missing something.
Any ideas?
EDIT:
Ok, so as daniel points out, the compile time checking of a runtime type is not practical.
What is my best option for doing what I want to do here? Namely, Treat all Foo's the same, but still have a relatively sane access mechanism?
EDIT2:
I don't want to convert the value to different types. I want to return the correct type for the value. That is, if it's a double, I don't want to return an int.
How about passing in the variable as a parameter to the get? Like this:
int i = foo.get(i);
Then in your class, you'd have something like:
public int get(int p) {
if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
return this.intVal;
}
public float get(float p) {
if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
return this.floatVal;
}
This sort of turns the type checking inside-out: instead of checking what type foo holds, you have foo check what type you want. If it can give you that type, it does, or else it throws a runtime exception.
I don't think this could work (giving you the compiler error you want)
What would you want this to do:
Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;
Is that a compiler error?
I think "treat them all the same" (at least the way you've described it) and "compile time error" are going to be mutually exclusive.
In any case, I think the "best way" is going to be a compromise between generics and inheritance. You can define a Foo<T> that is a subclass of Foo; then you can still have collections of Foo.
abstract public class Foo
{
// Common implementation
abstract public object ObjectValue { get; }
}
public class Foo<T> : Foo
{
public Foo(T initialValue)
{
Value = initialValue;
}
public T Value { get; set; }
public object ObjectValue
{
get { return Value; }
}
}
Many systems use a helper methods to return the alternate types just as the .net frameworks base object has the ToString() method
Choose which is the best base type for each of your object and provide To methods for other cases
e.g.
class Foo{
public Int32 Value { get; set; }
public Byte ToByte() { return Convert.ToByte(Value); }
public Double ToDouble() { return (Double)Value; }
public new String ToString() { return Value.ToString("#,###"); }
}
One thing is to store any type in your internal state of the class, and another is to expose it externally. When you write a class, you are actually declaring a contract for its behavior. The way you write it will influence greatly how client code will look like when using the class.
For example, by implementing the IConvertible interface you state that your type can be converted to any CLR type as an equivalent value.
I have also seen implementations where a Value class was used to store results of calculations, results that could represent either a string, double, int or boolean. But, the problem was that client code had to check a Value.Type property of an enum {String, Integer, Double, Boolean} and then either cast the Value.Value property (which was exposed externally by the Value class as an Object type) or use the specific ValueString, ValueDouble, ValueInt, ValueBoolean getters.
Why not just use string, double and int?
After info about collection: What about using object? You will have to check for types and such afterwards anyways. And to help you with that you can use the is and as operators. And the Enumerable.Cast Method, or even better, the Enumerable.OfType Method.
Actually, what is the purpose of this class? The biggest problem seems to be design breaking at least SRP (single responsibility principle).
Nonetheless, if I'm reading it correctly, you'd like to store some value in the container, pass the container to client and type-safely retrieve the value.
With this approach, you can use your proposal, i.e.
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
Foo a = new Foo();
a.SetValue(4);
Console.WriteLine(a.GetValue<int>());
Foo b = new Foo();
a.SetValue("String");
Console.WriteLine(a.GetValue<string>());
Console.ReadLine();
return 0;
}
}
class Foo {
private object value; // watch out for boxing here!
public void SetValue(object value) {
this.value = value;
}
public T GetValue<T>() {
object val = this.value;
if (val == null) { return default(T); } // or throw if you prefer
try {
return (T)val;
}
catch (Exception) {
return default(T);
// cast failed, return default(T) or throw
}
}
}
}
However, in that case why not simply pass data as object and cast by yourself?
Depending on your needs, you may also try "PHP in C#":
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
MyInt a = 1;
MyInt b = "2";
Console.WriteLine(a + b); // writes 3
Console.ReadLine();
return 0;
}
}
class MyInt {
private int value;
public static implicit operator int(MyInt container) {
return container.value;
}
public static implicit operator MyInt(int value) {
MyInt myInt = new MyInt();
myInt.value = value;
return myInt ;
}
public static implicit operator MyInt(string stringedInt) {
MyInt myInt = new MyInt();
myInt.value = int.Parse(stringedInt);
return myInt;
}
}
}
I'm sorry, I just don't buy your premise. If the data all have the same purpose, then they should all have the same type. Consider a class that's meant to hold the current temperature, as returned by one of several web services. All the services return the temperature in Centigrade. But one returns as an int, one returns as a double, and one returns it as a string.
It's not three different types - it's one type - double. You would simply need to convert the non-double returns into double, which is what the temperature is (or maybe float).
In general, if you have multiple representations of one thing, then it's still one thing, not multiple things. Convert the multiple representations into one.