This question already has answers here:
Check inside method whether some optional argument was passed
(10 answers)
Closed 2 years ago.
I hope someone can help me or give me an information if that is even possible...
I want to check in an Named-Method which parameters are really set and passed to it.
It would be possible to make an Dictionary and pass the parameter by an KeyValue-Pair to the Method, but is there an other solution?
Can i check the current method in the stack-trace and collect the current set argmuents of that method or something?
For better understanding, i created an example to visualize the issue:
class Program
{
static void Main(string[] args)
{
TestClass newC = new TestClass("Init", 10);
newC.ToString(); //{varStr: "Init"; varInt: 10}
newC.update(vStr: "ok");
newC.ToString(); //{varStr: "ok"; varInt: 0}
// !!! but should have {varStr: "ok"; varInt: 10} !!!
Console.WriteLine("<-- press any key to exit -->");
Console.ReadKey();
}
}
class TestClass
{
string varStr;
int varInt;
public TestClass(string vStr, int vInt)
{
varStr = vStr;
varInt = vInt;
}
public void update(string vStr = default, int vInt = default)
{
//here check it if vStr-Param was set and set varStr only if it was passed to it!
//TODO
varStr = vStr;
//here check it if vInt-Param was set and set varInt only if it was passed to it!
//TODO
varInt = vInt;
}
public override string ToString()
{
Console.WriteLine($"TestClass: varStr: {varStr}; varInt: {varInt};");
return null;
}
}
Is there any way to achieve this, like the way i think?
EDIT:
Use default value and check it!
The default value can also be set over this Method! So an solution
with checking the default value and only if that is equal than we set
the value, is no option for us.
We have also an "custom-class" which we give over that method and it
could also be nullable.
Use Overload methods!
The Problem is that this class has not 2 variables, it has over 15 and
it would be an big overhead to write each overload method.
SOLUTION:
I found a Solution without declare default values... so every Value is able to be set!
class TestClass
{
string varStr;
int varInt;
TestClass1 varCustomClass;
public TestClass(string vStr, int vInt, TestClass1 vCustomClass)
{
varStr = vStr;
varInt = vInt;
varCustomClass = vCustomClass;
}
public void update(Opt<int> varInt = default(Opt<int>),
Opt<string> varStr = default(Opt<string>),
Opt<TestClass1> varCustomClass = default(Opt<TestClass1>))
{
if (varStr.HasValue)
{
this.varStr = varStr.Value;
}
if (varInt.HasValue)
{
this.varInt = varInt.Value;
}
if (varCustomClass.HasValue)
{
this.varCustomClass = varCustomClass.Value;
}
}
public override string ToString()
{
Console.WriteLine($"TestClass3: varStr: {varStr}; varInt: {varInt}; varCustomClass: {varCustomClass};");
return null;
}
}
public struct Opt<T>
{
public Opt(T value)
{
_value = value;
_hasValue = true;
}
public static explicit operator T(Opt<T> optional)
{
return optional._value;
}
public static implicit operator Opt<T>(T value)
{
return new Opt<T>(value);
}
T _value;
public T Value
{
get { return _value; }
}
bool _hasValue;
public bool HasValue
{
get { return _hasValue; }
}
}
Might your string also be null? If not, you can check if it's null. For your int, you can use a nullable value type:
public void update(string vStr = null, int? vInt = null)
{
if(vStr != null)
{
varStr = vStr;
}
if(vInt != null)
{
varInt = vInt.Value;
}
}
If you have only a few parameters and they all have different types, overloaded methods are also a good solution:
public void update(string vStr)
{
varStr = vStr;
}
public void update(int vInt)
{
varInt = vInt;
}
public void update(string vStr, int vInt)
{
update(vStr);
update(vInt);
}
I have two constructors which feed values to readonly fields.
public class Sample
{
public Sample(string theIntAsString)
{
int i = int.Parse(theIntAsString);
_intField = i;
}
public Sample(int theInt) => _intField = theInt;
public int IntProperty => _intField;
private readonly int _intField;
}
One constructor receives the values directly, and the other does some calculation and obtains the values, then sets the fields.
Now here's the catch:
I don't want to duplicate the
setting code. In this case, just one
field is set but of course there may
well be more than one.
To make the fields readonly, I need
to set them from the constructor, so
I can't "extract" the shared code to
a utility function.
I don't know how to call one
constructor from another.
Any ideas?
Like this:
public Sample(string str) : this(int.Parse(str)) { }
If what you want can't be achieved satisfactorily without having the initialization in its own method (e.g. because you want to do too much before the initialization code, or wrap it in a try-finally, or whatever) you can have any or all constructors pass the readonly variables by reference to an initialization routine, which will then be able to manipulate them at will.
public class Sample
{
private readonly int _intField;
public int IntProperty => _intField;
private void setupStuff(ref int intField, int newValue) => intField = newValue;
public Sample(string theIntAsString)
{
int i = int.Parse(theIntAsString);
setupStuff(ref _intField,i);
}
public Sample(int theInt) => setupStuff(ref _intField, theInt);
}
Before the body of the constructor, use either:
: base (parameters)
: this (parameters)
Example:
public class People: User
{
public People (int EmpID) : base (EmpID)
{
// Add more statements here.
}
}
I am improving upon supercat's answer. I guess the following can also be done:
class Sample
{
private readonly int _intField;
public int IntProperty
{
get { return _intField; }
}
void setupStuff(ref int intField, int newValue)
{
//Do some stuff here based upon the necessary initialized variables.
intField = newValue;
}
public Sample(string theIntAsString, bool? doStuff = true)
{
//Initialization of some necessary variables.
//==========================================
int i = int.Parse(theIntAsString);
// ................
// .......................
//==========================================
if (!doStuff.HasValue || doStuff.Value == true)
setupStuff(ref _intField,i);
}
public Sample(int theInt): this(theInt, false) //"false" param to avoid setupStuff() being called two times
{
setupStuff(ref _intField, theInt);
}
}
Here is an example that calls another constructor, then checks on the property it has set.
public SomeClass(int i)
{
I = i;
}
public SomeClass(SomeOtherClass soc)
: this(soc.J)
{
if (I==0)
{
I = DoSomethingHere();
}
}
Yeah, you can call other method before of the call base or this!
public class MyException : Exception
{
public MyException(int number) : base(ConvertToString(number))
{
}
private static string ConvertToString(int number)
{
return number.toString()
}
}
Constructor chaining i.e you can use "Base" for Is a relationship and "This" you can use for same class, when you want call multiple Constructor in single call.
class BaseClass
{
public BaseClass():this(10)
{
}
public BaseClass(int val)
{
}
}
class Program
{
static void Main(string[] args)
{
new BaseClass();
ReadLine();
}
}
When you inherit a class from a base class, you can invoke the base class constructor by instantiating the derived class
class sample
{
public int x;
public sample(int value)
{
x = value;
}
}
class der : sample
{
public int a;
public int b;
public der(int value1,int value2) : base(50)
{
a = value1;
b = value2;
}
}
class run
{
public static void Main(string[] args)
{
der obj = new der(10,20);
System.Console.WriteLine(obj.x);
System.Console.WriteLine(obj.a);
System.Console.WriteLine(obj.b);
}
}
Output of the sample program is
50 10 20
You can also use this keyword to invoke a constructor from another constructor
class sample
{
public int x;
public sample(int value)
{
x = value;
}
public sample(sample obj) : this(obj.x)
{
}
}
class run
{
public static void Main(string[] args)
{
sample s = new sample(20);
sample ss = new sample(s);
System.Console.WriteLine(ss.x);
}
}
The output of this sample program is
20
Error handling and making your code reusable is key. I added string to int validation and it is possible to add other types if needed. Solving this problem with a more reusable solution could be this:
public class Sample
{
public Sample(object inputToInt)
{
_intField = objectToInt(inputToInt);
}
public int IntProperty => _intField;
private readonly int _intField;
}
public static int objectToInt(object inputToInt)
{
switch (inputToInt)
{
case int inputInt:
return inputInt;
break;
case string inputString:
if (!int.TryParse(inputString, out int parsedInt))
{
throw new InvalidParameterException($"The input {inputString} could not be parsed to int");
}
return parsedInt;
default:
throw new InvalidParameterException($"Constructor do not support {inputToInt.GetType().Name}");
break;
}
}
Please, please, and pretty please do not try this at home, or work, or anywhere really.
This is a way solve to a very very specific problem, and I hope you will not have that.
I'm posting this since it is technically an answer, and another perspective to look at it.
I repeat, do not use it under any condition. Code is to run with LINQPad.
void Main()
{
(new A(1)).Dump();
(new B(2, -1)).Dump();
var b2 = new B(2, -1);
b2.Increment();
b2.Dump();
}
class A
{
public readonly int I = 0;
public A(int i)
{
I = i;
}
}
class B: A
{
public int J;
public B(int i, int j): base(i)
{
J = j;
}
public B(int i, bool wtf): base(i)
{
}
public void Increment()
{
int i = I + 1;
var t = typeof(B).BaseType;
var ctor = t.GetConstructors().First();
ctor.Invoke(this, new object[] { i });
}
}
Since constructor is a method, you can call it with reflection. Now you either think with portals, or visualize a picture of a can of worms. sorry about this.
In my case, I had a main constructor that used an OracleDataReader as an argument, but I wanted to use different query to create the instance:
I had this code:
public Subscriber(OracleDataReader contractReader)
{
this.contract = Convert.ToString(contractReader["contract"]);
this.customerGroup = Convert.ToString(contractReader["customerGroup"]);
this.subGroup = Convert.ToString(contractReader["customerSubGroup"]);
this.pricingPlan= Convert.ToString(contractReader["pricingPlan"]);
this.items = new Dictionary<string, Member>();
this.status = 0;
}
So I created the following constructor:
public Subscriber(string contract, string customerGroup) : this(getSubReader(contract, customerGroup))
{ }
and this method:
private static OracleDataReader getSubReader(string contract, string customerGroup)
{
cmdSubscriber.Parameters[":contract"].Value = contract + "%";
cmdSubscriber.Parameters[":customerGroup"].Value = customerGroup+ "%";
return cmdSubscriber.ExecuteReader();
}
notes: a statically defined cmdSubscriber is defined elsewhere in the code; My main constructor has been simplified for this illustration.
In case you need to run something before calling another constructor not after.
public class Sample
{
static int preprocess(string theIntAsString)
{
return preprocess(int.Parse(theIntAsString));
}
static int preprocess(int theIntNeedRounding)
{
return theIntNeedRounding/100;
}
public Sample(string theIntAsString)
{
_intField = preprocess(theIntAsString)
}
public Sample(int theIntNeedRounding)
{
_intField = preprocess(theIntNeedRounding)
}
public int IntProperty => _intField;
private readonly int _intField;
}
And ValueTuple can be very helpful if you need to set more than one field.
NOTE: most of the solutions above does not work for structs.
Unfortunately initializing struct fields in a method called by a constructor is not recognized by the compiler and will lead to 2 errors:
in the constructor: Field xxxx must be fully assigned...
in the method, if you have readonly fields: a read-only field cannot be assigned except in a constructor.
These can be really frustrating for example when you just need to do simple check to decide on which constructor to orient your call to.
This question already has answers here:
A property or indexer may not be passed as an out or ref parameter
(9 answers)
Closed 5 years ago.
So I have this class that holds 3 counters:
public class Files
{
private static ObservableCollection<Files> _files = new ObservableCollection<Files>();
private static int _inProcess;
private static int _finished;
private static int _inQueue;
public static ObservableCollection<Files> List
{
get { return _files ; }
set { _files = value; }
}
public static int InProcess
{
get { return _inProcess; }
set
{
_inProcess = value;
}
}
public static int Finished
{
get { return _finished; }
set
{
_finished = value;
}
}
public static int InQueue
{
get { return _inQueue; }
set
{
_inQueue = value;
}
}
}
And from another class I want to add value to this fields:
Interlocked.Increment(ref Files.InProcess);
But got this error:
A property or indexer may not be passed as an out or ref parameter.
This works fine:
Files.InProcess++;
How can i fix it ?
The error is pretty straightforward. You can't pass a property as ref. In this case the best option is to create a method
public static void IncrementInProcess()
{
Interlocked.Increment(ref _inProcess);
}
i have one class with constractor like this
public class product_new : NK.Objects._product_new
{
private int Count_Per_Page;
public product_new(int count_per_page)
{
this.Count_Per_Page = count_per_page;
}
public int CountOP///////count of pages
{
get
{
return number_of_pages(Count_Per_Page);
}
}
as you see the CountOP is return a int value and it is connect to sql database to return this value.
private int number_of_pages(int tedad_per_pages)
{
return Q.Get_Back_Number_Of_Pages(
tedad_per_pages,
tbl_name,
"",
new Queries.Cmd_Parameters());
}
in several time if create object from this class the CountOP is not changed but the function number_of_pages is released and connect to the sql database.
how can i cache this variable?
Try using static Dictionary<int, int> - one dictionary for all the instances:
public class product_new : NK.Objects._product_new {
// Simplest, but not thread safe; use ConcurrentDictionary for thread safe version
private static Dictionary<int, int> s_KnownAnswers = new Dictionary<int, int>();
// Lazy: do not execute expensive operation eagerly: in the constructor;
// but lazyly: in the property where we have to perform it
public int CountOP {
get {
int result = 0;
// do we know the answer? If yes, then just return it
if (s_KnownAnswers.TryGetValue(Count_Per_Page, out result))
return result;
// if no, ask RDMBS
result = number_of_pages(Count_Per_Page);
// and store the result as known answer
s_KnownAnswers.Add(Count_Per_Page, result);
return result;
}
}
...
}
Introduce a private backing-field that holds the value and initialize its value within your constructor. Now you can return the variables value within your getter instead of hitting the database every time you call the getter.
public class product_new : NK.Objects._product_new
{
private int Count_Per_Page;
private readonly int _CountOP;
public product_new(int count_per_page)
{
this.Count_Per_Page = count_per_page;
this._CountOP = number_of_pages(count_per_page);
}
public int CountOP///////count of pages
{
get
{
return this._CountOP;
}
}
Apart from this I strongly suggest to have a look at Mircrsofts naming-conventions.
Change to use a backed property:
private int _npages = -1;
public int CountOP///////count of pages
{
get
{
if(_npages == -1)
_npages = number_of_pages(Count_Per_Page);
return _npages;
}
}
Consider this code:
public string Variable1 { get; set;}
public int Variable2 { get; set;}
public void Function()
{
// Has been Variable1 Initialized?
}
Inside the function, I want to know if a value has been sent to Variable1 & Variable2, prior to the function call,
even if the DEFAULT values have been sent, that's ok (null for string & 0 for int)
Consider using a simple wrapper like this:
public struct AssignableProperty<T>
{
private T _value;
public T Value
{
get { return _value; }
set
{
WasAssigned = true;
_value = value;
}
}
public bool WasAssigned { get; private set; }
public static implicit operator AssignableProperty<T>(T data)
{
return new AssignableProperty<T>() { Value = data };
}
public static bool operator ==(AssignableProperty<T> initial, T data)
{
return initial.Value.Equals(data);
}
public static bool operator !=(AssignableProperty<T> initial, T data)
{
return !initial.Value.Equals(data);
}
public override string ToString()
{
return Value.ToString();
}
}
Then your class'll look like this:
public class Test
{
public AssignableProperty<string> Variable1 { get; set; }
public AssignableProperty<int> Variable2 { get; set; }
public void Function()
{
if(Variable1.WasAssigned&&Variable2.WasAssigned)
//do stuff
}
}
You can go further and add throw Exception or contract to getter, so if somebody'll try to access uninitialized value it'll throw an exception or show you warning
Some basics about default value in C#:
When an instance of a class (or struct) is created, all fields are initialized to their respective default value.
For reference types, it will be null. For value types, it will be equivalent to 0. This is easily explains as the memory management ensures that new allocated memory is initialized to 0x0 bytes.
Auto-properties hide the generated field, but there is one. So the same rules apply.
Now to answer your question, the best way to make sure that values are initialized is to make a constructor with one parameter for each field/property and to hide the default constructor with no parameters:
public Yourtype(String param1, Int32 param2)
{
this.Variable1 = param1;
this.Variable2 = param2;
}
private Yourtype() { }
Other alternatives is described in #Sean and #Alex answers if only a subset of properties/fields needs to be initialized/checked. But this hides some overhead (one bool for each property/field and some indirection).
For the reference types you'll need to add a flag:
string m_Variable1;
bool m_IsVariable1Set;
public string Variable1
{
get{return m_Variable1;}
set{m_IsVariable1Set = true; m_Variable1 = value;}
}
For the value types you can use a nullable value
int? m_Variable2;
int Variable2
{
get{return m_Variable2.GetValueOrDefault();}
set{m_Variable2 = value;}
}
Which you can then check to see if it's been set by using m_Variable2.HasValue.
Well you can simply do a check on both variables to see if they have any value assigned to them in your function
public void Function()
{
if (String.IsNullOrEmpty(Variable1) && Variable2 ==0 )
{
// Variables are not assigned
}
}