Returning value from setter - c#

I have an immutable struct and would like to keep it immutable, but also allow schematics like var p2 = p1.v = 3. I thought that the following might work, but it appears not:
public struct Number {
readonly int n;
public int N {
get{ return n; }
set{ return new Number(value); }
}
public Number(int newN) {
n = newN;
}
}
Is there any way to get var p2 = p1.v = 3 or var p2 = (p1.v = 3) to work?

No, there is no syntax like this that will work. Setters are, well, setters, not a way to get something.

First of all you want to do something that no one will be able to read. If you structure is immutable what one should expect as result of p1.v = 3? Obviously p1 should not change, no one expect setter to return value... the only reasonable behavior would be to see an exception "This object is immutable", but than lack of setter would be much better indication of the property being read only....
Possibly you trying to implement something like fluent interface which is much more common:
var newValue = oldValue.WithOneProperty(5).WithOtherProperty(3);
class Number
{
int oneProperty;
int otherProperty;
Number WithOneProperty(int v) { return new Number(v, this.otherProperty); }
Number WithOtherProperty(int v) { return new Number(this.oneProperty, v); }
}

You should only return values from getters.

I think there is a valid use for this with one-time-tokens or keys. I used some of the code here to generate this:
public class MyController : Controller
{
private static string m_oneTimeKey = "this key hasn't been initialised yet";
private string oneTimeKeyGet()
{
// Return the last key
string returnValue = MyController.m_oneTimeKey;
// Generate a new random key so the one we return can't be reused
var m_oneTimeKey = GetRandomString();
return returnValue;
}
private string oneTimeKeySet()
{
// Generate a new random key
var newValue = GetRandomString();
m_oneTimeKey = newValue;
return newValue;
}
private string GetRandomString()
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var returnValue = new string(
Enumerable.Repeat(chars, 8)
.Select(s => s[random.Next(s.Length)])
.ToArray());
return returnValue;
}
And then I use it with:
var myURL = "/Index?id=" + ID + "&key=" + oneTimeKeySet();
And within the ActionResult I can verify if this is the one time call with:
public ActionResult Index(Guid id, string key)
{
if (key == oneTimeKeyGet() {
....
}
}
}
I've actually gone a step further and I also have a static key that is passed between functions that is also checked for in the if in the ActionResult.

Related

Comparison between three members of an object

Consider objects of the following type:
public class MyObject
{
// "defining" attributes
private string member1;
private string member2;
private string member3;
// other attributes
private string member4;
private string member5;
// ctor
public MyObject(){}
public bool compare(MyObject that)
{
// compare this object with another (that)
}
The compare() method should behave as follows. It only considers "defining" attributes. If they are all different between two objects, it should return false. If they are all the same, return false. In other cases, return true (if only one or two of them differ between the two objects).
The question is, do I have to resort to a huge if statement for this? Is there a "better" solution?
Instead of creating n number of strings, you can create property called List<string> DefiningAttributes and List<string> OtherAttributes.
Now add values to this lists where you want, for now I am doing it in constructor. Use Except() method to get difference from DefiningAttributes and OtherAttributes
Check below implementation
public class MyObject
{
// "defining" attributes
public List<string> DefiningAttributes { get; set; }
// other attributes
public List<string> OtherAttributes { get; set; }
public MyObject()
{
//I used constructor to assign values
DefiningAttributes = new List<string>() { "ABC", "PQR", "XYZ" };
OtherAttributes = new List<string>() { "ABC", "PQR", "Stackoverflow" };
}
public bool compare(MyObject that)
{
var difference = this.DefiningAttributes.Except(that.DefiningAttributes);
//Return false If they are all different between two objects OR if they are all same
if(difference.Count() == this.DefiningAttributes.Count() || !difference.Any())
return false;
//Otherwise return true
return true;
}
}
For more details, read Enumerable.Except method
I think this should do it
var comp1 = this.member1 == that.member1;
var comp2 = this.member2 == that.member2;
var comp3 = this.member3 == that.member3;
var comparisons = new List<string>() { comp2, comp3 };
return comparisons.Any(val => val != comp1 );
comp1, comp2 and comp3 will be bools. If any of those comparisons are not the same as the first comparison*, we know we have different results.
[*] You could use any reference point instead of the first comparison
Edit: Whoops, I thought this was a javascript question, but I then realized it was C#. I just changed my answer to use C# syntax, but the idea is the same. This requires the Linq extension method Any.
The following code should do the trick.
If you want to increase the number of defining properties you just edit the size of the array or swap it to a list.
It should iterate over them and when one does not mach return true.
If at the end none matches returns false.
public class MyObject
{
// "defining" attributes
public string[] definingAttributes = new string[3];
// other attributes
private string member4;
private string member5;
// ctor
public MyObject() { }
public bool compare(MyObject that)
{
bool? previousResult = null;
// compare this object with another (that)
for (int i = 0; i < definingAttributes.Length; i++)
{
if (previousResult == null)
{
previousResult = definingAttributes[i] == that.definingAttributes[i];
}
if (definingAttributes[i] != that.definingAttributes[i])
{
if (previousResult != (definingAttributes[i] == that.definingAttributes[i]))
{
return true;
}
}
}
return false;
}
}

How to make Dictionary find object key by value

In my application i need to use custom object as a key for dictionary.
The problem is compare by reference,
like we all know when using value types the compare work comparing by values but in objects it's compare by reference so even if the objects are equal they sored in different places in memory heap so it returns false
to do it right i need to override Equals and GetHashCode methods (i think correct me if i'm wrong)
i override the Equals Method and it's working:
bool IsEqual = dictionaryList.Keys.First().Equals(compareKey); returns true.
what i didn't know is how to override the GetHashCode method (and if i need) to my case.
Exception that i get :
The given key was not present in the dictionary. -
The given key was not present in the dictionary.
How can i solve that issue or maybe i doing it completely in wrong way...
Thank's
using System;
using System.IO;
using System.Threading;
using System.Linq;
using System.Collections.Generic;
public sealed class Program
{
public class Options
{
public string x { get; set; }
public string y { get; set; }
}
public class Data
{
public string myData { get; set; }
}
public class KeyClass
{
public int KeyNumber { get; set; }
public List<Options> Options { get; set; }
public override bool Equals(object obj)
{
KeyClass keyClass = obj as KeyClass;
bool IsKeyNumberEqual = (KeyNumber == keyClass.KeyNumber);
bool IsOptionsEqual = true;
if (!(Options.Count == keyClass.Options.Count) || !IsKeyNumberEqual)
{
IsOptionsEqual = false;
}
else
{
for (int i = 0; i < Options.Count; i++)
{
if (!(Options[i].x == keyClass.Options[i].x) ||
!(Options[i].y == keyClass.Options[i].y))
{
IsOptionsEqual = false;
break;
}
}
}
return (IsKeyNumberEqual && IsOptionsEqual);
}
}
public static void Main()
{
try
{
List<Options> optionsList = new List<Options>();
optionsList.Add(new Options() { x = "x1", y = "y1" });
optionsList.Add(new Options() { x = "x2", y = "y2" });
Data someData = new Data() { myData = "someData" };
Data getData = new Data();
KeyClass dictionaryKey = new KeyClass() { KeyNumber = 1, Options = optionsList };
KeyClass compareKey = new KeyClass() { KeyNumber = 1, Options = optionsList };
Dictionary<KeyClass, Data> dictionaryList = new Dictionary<KeyClass, Data>();
dictionaryList.Add(dictionaryKey, someData);
bool IsEqual = dictionaryList.Keys.First().Equals(compareKey);
getData = dictionaryList[compareKey];
}
catch (Exception ex)
{
string exMessage = ex.Message;
}
}
}
to do it right i need to override Equals and GetHashCode methods (i think correct me if i'm wrong)
You're correct. .NET requires that two objects that compare as equal have the same hash code. This is not limited to dictionaries.
The trivial implementation is to make every object return the same hash code. But although two different objects are allowed to have the same hash code, you should keep this to a minimum. When you have a lot of hash collisions, performance of dictionaries and other containers will be worse.
A slightly better implementation would be to return KeyNumber (or KeyNumber.GetHashCode()). This can be a good enough implementation if you almost never have identical key numbers, if identical key numbers is a very strong indication that the options will be identical as well.
The best implementation would be to combine the hash codes of KeyNumber and all your Options values, as in Matthew Watson's answer.
You need to write a GetHashCode() that includes everything that contributes to the Equals() method.
For example:
public override int GetHashCode()
{
unchecked
{
int hash = KeyNumber * 397;
foreach (var opt in Options)
{
hash = hash*23 + opt.x.GetHashCode();
hash = hash*23 + opt.y.GetHashCode();
}
return hash;
}
}
If you implement GetHashCode() for your Options class, for example:
public class Options
{
public readonly string x;
public readonly string y;
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode();
}
}
Then you can write GetHashCode() more simply:
public override int GetHashCode()
{
unchecked
{
int hash = KeyNumber * 397;
foreach (var opt in Options)
hash = hash*23 + opt.GetHashCode();
return hash;
}
}
One important thing I forgot to mention earlier:
It is MOST IMPORTANT that none of your fields that contribute to equality or hash code are changed after the object has been put into the dictionary.
If you change any of them after adding the object to the dictionary, it's likely that you will no longer be able to retrieve the object from the dictionary.
The best way to ensure this is to use only immutable fields for equality and hash code.

Creating a Session Helper Class that uses an array object C#

I am using a session helper class to track more than several variable. So far I have 30 that are needed from page to page, not all at once of course. I need to convert some of the values from single to array. The Session helper class I use is as follows. For brevity I have shown only two session variables we use for tracking tab index for two accordions.
using System;
using System.Globalization;
using System.Linq;
using System.Web;
public class SessionHelper
{
//Session variable constants
public const string AccordionTop = "#tabTop";
public const string AccordionBot = "#tabBot";
public static T Read<T>(string variable)
{
object value = HttpContext.Current.Session[variable];
if (value == null)
return default(T);
else
return ((T)value);
}
public static void Write(string variable, object value)
{
HttpContext.Current.Session[variable] = value;
}
public static int TabTop
{
get
{
return Read<int>(AccordionTop);
}
set
{
Write(AccordionTop, value);
}
}
public static int TabBot
{
get
{
return Read<int>(AccordionBot);
}
set
{
Write(AccordionBot, value);
}
}
}
So on each page I can work with variables easily as follows:
To Write:
SessionHelper.TabTop = 1; or SessionHelper.TabBot = 3
To Read:
If (SessionHelper.TabTop……….)
This all works fine. I now want to extend this to array values held in session. The array contains int, string and date time value.
For the array session object I have tried adding:
public class SessionHelper
{
public const string CompInfo = "CompAccInfo";
public static T ReadArray<T>(string variable)
{
object[] result = HttpContext.Current.Session[variable] as object[];
if (result == null)
{
return default(T);
//result = new object[30];
}
else
return ((T)(object)result);
}
public static void WriteArray(string variable, object[] value)
{
HttpContext.Current.Session[variable] = value;
}
public static object[] CompDetails
{
get
{
return ReadArray<object[]>(CompInfo);
}
set
{
WriteArray(CompInfo, value);
}
}
}
But then I get an “Object reference not set to…… error when I try to do this:
public void EGetCompanyInformation(MasterPage myMaster, int entityCode)
{
int prevEntity = 0;Using (sqlconnetiooo
.....
//I get values here this works fine
//Then:
sqlr = cmd.ExecuteReader();
sqlr.Read();
if (sqlr.HasRows)
{
//Calculate accounting period adjustment.
yearEndDiff = 12 - Convert.ToInt32(sqlr.GetDateTime(5).Month);
//Company Code.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
//Company Name.
SessionHelper.CompDetails[1] = sqlr.GetString(1);
//Currency Unit.
SessionHelper.CompDetails[2] = sqlr.GetString(2);
//Base Currency Code.
SessionHelper.CompDetails[3] = sqlr.GetString(3);
//Reporting Currency Code.
SessionHelper.CompDetails[4] = sqlr.GetString(4);
//Company Year End.
SessionHelper.CompDetails[5] = yearEndDiff;
//Country Code.
SessionHelper.CompDetails[6] = sqlr.GetString(6);
//Country Name.
SessionHelper.CompDetails[7] = sqlr.GetString(7);
//Base Currency Name.
SessionHelper.CompDetails[8] = sqlr.GetString(8);
//Report Currency Name.
SessionHelper.CompDetails[9] = sqlr.GetString(9);
//ClientID.
SessionHelper.CompDetails[10] = sqlr.GetInt32(10);
Other code here
}
}
It seems any SessionHelper.CompDetails[i] does not work : Error Object reference not set to an instance of an object.
What will happen if ReadArray will return default(T)? It will return null. Than access to any object by index inside the array will cause the exception you face.
It is not quite obvious what your code is intended to do.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
What do you want here? CompDetails itself should return an array. But you are trying to rewrite it immediately by some values.
If you want to access the CompDetails and rewrite it's objects than you have to instantiate it by
int n = 10;
SessionHelper.CompDetails = new CompDetails[n];
default(object[]) will always throw null. because the array of object is reference type and default value of any reference type is null. So accessing null value will get you Object reference not set to an instance of object.
You can change your old implementation like below:
public static T Read<T>(string variable, int arraySize=10)
{
object value = HttpContext.Current.Session[variable];
if(typeof(T).IsArray && value == null)
{
//array requires size I personally prefer to have
//differnt read method for array.
return ((T)Activator.CreateInstance(typeof(T),arraySize));
}
if(!typeof(T).IsValueType && value == null)
{
//if it is not value type you can return new instance.
return ((T)Activator.CreateInstance(typeof(T)));
}
else if (value == null)
return default(T);
else
return ((T)value);
}
And access SessionHelper as below:
var sessionarray = SessionHelper.Read<object[]>("myarray",15);
....
// then use that sessionarray here.
....
You have to instantiate the CompDetails array before you start assigning values to it.
if (sqlr.HasRows)
{
//Calculate accounting period adjustment.
yearEndDiff = 12 - Convert.ToInt32(sqlr.GetDateTime(5).Month);
// Instantiate array
SessionHelper.CompDetails = new object[11];
//Company Code.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
// etc

How to check CONTAINS with multiple values

I am trying to find all the zones that contain 2 or more zone members where the search term is a string value. Here is the code I have. In the FindCommmonZones method when I try to cast the result of an Intersect to an ObservableCollection I get a run-time on an invalid cast. The question is, is there a better way to do this? The string array that is the paramter for FindCommonZones() can be any count of strings. StackOverflow had some other similar posts but none really answered my question - it looked like they all pertained more to SQL.
Some code:
public class Zone
{
public List<ZoneMember> MembersList = new List<ZoneMember>();
private string _ZoneName;
public string zoneName{ get{return _ZoneName;} set{_ZoneName=value;} }
public Zone ContainsMember(string member)
{
var contained = this.MembersList.FirstOrDefault(m => m.MemberWWPN.
Contains(member) || m.MemberAlias.Contains(member));
if (contained != null) { return this; }
else { return null; }
}
}
public class ZoneMember
// a zone member is a member of a zone
// zones have ports, WWPNs, aliases or all 3
{
private string _Alias = string.Empty;
public string MemberAlias {get{return _Alias;} set{_Alias = value; } }
private FCPort _Port = null;
public FCPort MemberPort { get { return _Port; } set { _Port = value; } }
private string _WWPN = string.Empty;
public string MemberWWPN { get { return _WWPN; } set { _WWPN = value; } }
private bool _IsLoggedIn;
public bool IsLoggedIn { get { return _IsLoggedIn; } set { _IsLoggedIn = value; } }
private string _FCID;
public string FCID {get{return _FCID;} set{ _FCID=value; } }
}
private ObservableCollection<ZoneResult> FindCommonZones(string[] searchterms)
{
ObservableCollection<ZoneResult> tempcollection =
new ObservableCollection<ZoneResult>();
//find the zones for the first search term
tempcollection = this.FindZones(searchterms[0]);
//now search for the rest of the search terms and compare
//them to existing result
for (int i = 1; i < searchterms.Count(); i++ )
{
// this line gives an exception trying to cast
tempcollection = (ObservableCollection<ZoneResult>)tempcollection.
Intersect(this.FindZones(searchterms[i]));
}
return tempcollection;
}
private ObservableCollection<ZoneResult> FindZones(string searchterm)
// we need to track the vsan where the zone member is found
// so use a foreach to keep track
{
ObservableCollection<ZoneResult> zonecollection = new ObservableCollection<ZoneResult>();
foreach (KeyValuePair<int, Dictionary<int, CiscoVSAN>> fabricpair in this.FabricDictionary)
{
foreach (KeyValuePair<int, CiscoVSAN> vsanpair in fabricpair.Value)
{
var selection = vsanpair.Value.ActiveZoneset.
ZoneList.Select(z => z.ContainsMember(searchterm)).
Where(m => m != null).OrderBy(z => z.zoneName);
if (selection.Count() > 0)
{
foreach (Zone zone in selection)
{
foreach (ZoneMember zm in zone.MembersList)
{
ZoneResult zr = new ZoneResult(zone.zoneName,
zm.MemberWWPN, zm.MemberAlias, vsanpair.Key.ToString());
zonecollection.Add(zr);
}
}
}
}
}
return zonecollection;
}
Intersect is actually Enumerable.Intersect and is returning an IEnumerable<ZoneResult>. This is not castable to an ObservableCollection because it isn't one - it is the enumeration of the intersecting elements in both collections.
You can, however create a new ObservableCollection from the enumeration:
tempcollection = new ObservableCollection<ZoneResult>(tempcollection
.Intersect(this.FindZones(searchterms[i]));
Depending on how many elements you have, how ZoneResult.Equals is implemented, and how many search terms you expect, this implementation may or may not be feasable (FindZones does seem a little overly-complicated with O(n^4) at first glance). If it seems to be a resource hog or bottleneck, it's time to optimize; otherwise I would just leave it alone if it works.
One suggested optimization could be the following (incorporating #Keith's suggestion to change ContainsMember to a bool) - although it is untested, I probably have my SelectManys wrong, and it really largely amounts to the same thing, you hopefully get the idea:
private ObservableCollection<ZoneResult> FindCommonZones(string[] searchterms)
{
var query = this.FabricDictionary.SelectMany(fabricpair =>
fabricpair.Value.SelectMany(vsanpair =>
vsanpair.Value.ActiveZoneSet.ZoneList
.Where(z=>searchterms.Any(term=>z.ContainsMember(term)))
.SelectMany(zone =>
zone.MembersList.Select(zm=>new ZoneResult(zone.zoneName, zm.MemberWWPN, zm.MemberAlias, vsanpair.Key.ToString()))
)
)
.Distinct()
.OrderBy(zr=>zr.zoneName);
return new ObservableCollection<ZoneResult>(query);
}

Is there a good C# design pattern for parsing strings that when split have different amounts of data?

I am dealing with values delimited by commas sent to me as a string. The strings come in many different structures (meaning different data types in different locations of the string as well as varying amounts of data). So while one string might be represented as:
- common data,identifier,int,string,string,string.
Another might be represented as:
- common data,identifier,int,int,string,string,string.
Design goals:
Common parse method
Common validation (i.e. int.TryParse() returns true)
Readily able to add different structures
Is there a good design pattern, or combination of design patterns, that allows me to parse the values, check them, and return an object only if the right amount of values were pulled in and those values were the expected data types?
Note: I am dealing with more than 30 different string structures.
If all the lines start with common data, identifier, and then are followed by a variable but expected (i.e. known based on the identifier) set of values, then a table approach could work well. To continue your example, say you have two different types:
common data,identifier,int,string,string,string.
common data,identifier,int,int,string,string,string.
You can build a class that defines what you're looking for:
class ItemDesc
{
public string Ident { get; private set; }
public string Fields { get; private set; }
public ItemDesc(string id, string flds)
{
Ident = id;
Fields = flds;
}
}
The Fields property is just a string that contains one-character type descriptions for the variable data. That is, "isss" would be interpreted as int,string,string,string.
You can then build a Dictionary<string, ItemDesc> that you can use to look these up:
Dictionary<string, ItemDesc> ItemLookup = new Dictionary<string, ItemDesc>
{
{ "ItemType1", new ItemDesc("ItemType1", "isss") },
{ "ItemType2", new ItemDesc("ItemType2", "iisss") },
};
Now when you read a line, use string.Split() to split it into fields. Get the identifier, look it up the dictionary to get the item descriptions, and then parse the rest of the fields. Something like:
string line = GetLine();
var fields = line.Split(',');
// somehow get the identifier
string id = GetIdentifier();
ItemDesc desc;
if (!ItemLookup.TryGetValue(id, out desc))
{
// unrecognized identifier
}
else
{
int fieldNo = 3; // or whatever field is after the identifier
foreach (var c in desc.Fields)
{
switch (c)
{
case 'i' :
// try to parse an int and save it.
break;
case 's' :
// save the string
break;
default:
// error, unknown field type
break;
}
++fieldNo;
}
}
// at this point if no errors occurred, then you have a collection
// of parsed fields that you saved. You can now create your object.
would need little more details, based on your problem domain it could entirely change. but following seem to be the first set of patterns, they are ordered on suitability.
Interpreter
Strategy
Builder
Just split them using string.Split(), and then int.Parse() or int.TryParse() each int value in the resulting array as needed.
var myStrings = string.Split(sourceString);
int myint1 = int.Parse(myStrings[0]);
There are several ways of dealing with this. Here's a simple one (outputting just an object array):
class Template
{
// map identifiers to templates
static Dictionary<string, string> templates = new Dictionary<string, string>
{
{ "type1", "isss" },
{ "type2", "iisss" },
};
static bool ParseItem(string input, char type, out object output)
{
output = null;
switch (type)
{
case 'i':
int i;
bool valid = int.TryParse(input, out i);
output = i;
return valid;
case 's':
output = input;
return true;
}
return false;
}
public static object[] ParseString(string input)
{
string[] items = input.Split(',');
// make sure we have enough items
if (items.Length < 2)
return null;
object[] output = new object[items.Length - 2];
string identifier = items[1];
string template;
// make sure a valid identifier was specified
if (!templates.TryGetValue(identifier, out template))
return null;
// make sure we have the right amount of data
if (template.Length != output.Length)
return null;
// parse each item
for (int i = 0; i < template.Length; i++)
if (!ParseItem(items[i + 2], template[i], out output[i]))
return null;
return output;
}
}
If you're interested in returning actual objects instead of just object arrays, you can put metadata into the class definitions of the objects you're returning. Then when you get the object type you look for the metadata to figure out where to find its value in the input array. Here's a quick example:
namespace Parser
{
// create metadata attribute
class CsvPositionAttribute : Attribute
{
public int Position { get; set; }
public CsvPositionAttribute(int position)
{
Position = position;
}
}
// define some classes that use our metadata
public class type1
{
[CsvPosition(0)]
public int int1;
[CsvPosition(1)]
public string str1;
[CsvPosition(2)]
public string str2;
[CsvPosition(3)]
public string str3;
}
public class type2
{
[CsvPosition(0)]
public int int1;
[CsvPosition(1)]
public int int2;
[CsvPosition(2)]
public string str1;
[CsvPosition(3)]
public string str2;
[CsvPosition(4)]
public string str3;
}
public class CsvParser
{
public static object ParseString(string input)
{
string[] items = input.Split(',');
// make sure we have enough items
if (items.Length < 2)
return null;
string identifier = items[1];
// assume that our identifiers refer to a type in our namespace
Type type = Type.GetType("Parser." + identifier, false);
if (type == null)
return null;
object output = Activator.CreateInstance(type);
// iterate over fields in the type -- you may want to use properties
foreach (var field in type.GetFields())
// find the members that have our position attribute
foreach (CsvPositionAttribute attr in
field.GetCustomAttributes(typeof(CsvPositionAttribute),
false))
// if the item exists, convert it to the type of the field
if (attr.Position + 2 >= items.Length)
return null;
else
// ChangeType may throw exceptions on failure;
// catch them and return an error
try { field.SetValue(output,
Convert.ChangeType(items[attr.Position + 2],
field.FieldType));
} catch { return null; }
return output;
}
}
}

Categories