Does C# have static local variables like C++?

  • Thread starter Thread starter Zytan
  • Start date Start date
Zytan said:
I noticed that I can't do this:
const int x = MyMethod();
because MyMethod() isn't constant.

But, what I want to do is that after x is assigned, not allow it to
change. C# doesn't allow that. I wonder why.

You can't do that for a local variable, but you can for a member
variable. I've never wanted to do it for a local variable - it's easy
enough to make sure that you never change it, because there shouldn't
be too much code to look through.
 
Jon Skeet said:
I don't think that's quite right. If the Sub/Proc is a Shared one, it
becomes a "static variable" (C# terminology) but if it's not, it
becomes an "instance variable" (C# terminology). At least, that's what
my experimentation shows. Compile this as a DLL:

Public Class Foo

Public Sub First()
Static inFirst As Integer = 0
End Sub

Public Shared Sub Second ()
Static inSecond As Integer = 0
End Sub

End Class

The generated $STATIC$First$2001$inFirst variable is an instance
variable, but $STATIC$Second$001$inSecond is a static variable.


True, it would otherwise be impossible to access the shared (static) class-level variable, I
would have hoped the compiler to produce a warning as it's clearly not the intend of the
author of the method to declare a local as being shared (static )when it turns out it's not.
Note that even in this particular case, the compiler goes to a great lengths to protect the
variable during initialization, by generating some helper code which wraps the
initialization by a Monitor.Enter/Exit construct, really funny.

Willy.
 
Willy Denoyette said:
True, it would otherwise be impossible to access the shared (static) class-level variable,
I would have hoped the compiler to produce a warning as it's clearly not the intend of the
author of the method to declare a local as being shared (static )when it turns out it's
not.
Note that even in this particular case, the compiler goes to a great lengths to protect
the variable during initialization, by generating some helper code which wraps the
initialization by a Monitor.Enter/Exit construct, really funny.

Willy.

C++/CLI also supports local statics and follows the initialization behavior as imposed by
the C++ Standard, that means it's not thread safe.

Following class:
public ref class C
{
public:
C(){}
static void Foo()
{
static int i = 1;
}
};

Produces a static with global scope, irrespective the method's declaration (static or
other).

Willy.
 
True, visibility = lexical scope. What i really was trying to say was that the extend (or
lifetime) was different.

I think you mean "extent" :) I don't know why we all don't just use
'visible' and 'lifetime', rather than 'scope' and 'extent'.
Consider this small sample in vb.net:

Public Class C
Public Sub Foo(item As Integer)
Static staticLocal As Integer = -1
staticLocal = item
End Sub
End Class

Ok, so statics are allowed in VB, right.
here "staticLocal " is declared as a static local, that means that "staticLocal " lexical
scope is equal to it's active binding in program code, that is from the beginning of Foo
till Foo returns, which is also it's visibility as far as Foo is concerned.
Now, the variable itself is a static (and really a type variable), as such has it's extend
(lifetime) bound to that of the containing AD on the CLR. It's starts life when the class
loads and ends when to AD unloads.

Right, I understand the difference between scope and extent.

AD = ?

Zytan
 
I noticed that I can't do this:
You can't do that for a local variable, but you can for a member
variable. I've never wanted to do it for a local variable - it's easy
enough to make sure that you never change it, because there shouldn't
be too much code to look through.

Aaaah, and this is where you are doing the job that the compiler could
do. C# is robust and forces good programming, but this is one place
where VB and C# are lacking. I bet C# also disallows const parameters
like I frustrating found that VB doesn't have? I had a nice
discussion about this in the VB newsgroups.

So C# isn't so perfect after all. :)

I won't get into detail here, but the use of "const" offers many, many
wonderful features that people just don't know about until they use a
language that offers it, like C++. It's a godsend. You don't miss it
until you have it, and only then do you realize what it brings. I'll
find the link to the VB newsgroup thread if any wish to know about
them all.

Zytan
 
Zytan said:
I think you mean "extent" :) I don't know why we all don't just use
'visible' and 'lifetime', rather than 'scope' and 'extent'.

Right, extent.
Scope and visibility are most often used and this since a very long time in computer lanage
parlance.
A variables scope is most often related to it's lifetime, that is , how long that variable
exists in computers memory, most often the scope is the same as it's visibility, but there
are exceptions. That's why I said that in the case of a "local static" variable, that it's
"visibility" is not the same as it's scope, the variable doesn't cease to exist when it
leaves it's enclosing lexical scope.
Anyway, this is a can of worms I'm not going to open any further ;-)
Ok, so statics are allowed in VB, right.


Right, I understand the difference between scope and extent.

AD = ?

Application Domain.

Willy.
 
Zytan said:
Aaaah, and this is where you are doing the job that the compiler could
do. C# is robust and forces good programming, but this is one place
where VB and C# are lacking. I bet C# also disallows const parameters
like I frustrating found that VB doesn't have? I had a nice
discussion about this in the VB newsgroups.

So C# isn't so perfect after all. :)

I won't get into detail here, but the use of "const" offers many, many
wonderful features that people just don't know about until they use a
language that offers it, like C++. It's a godsend. You don't miss it
until you have it, and only then do you realize what it brings. I'll
find the link to the VB newsgroup thread if any wish to know about
them all.


Don't forget that .NET (the CLI) is a multi language platform, imposing const correctness in
one language makes no sense if it's not enforced by all languages available for the
platform, more, it makes little sense if not enforced by the CLR.
Even C++ (the single language) doesn't enforce, const-correctness, it even provides a
facility to cast-away const.
Even if C# and VB were the only languages on .NET, it was simply not possible to enforce
const correctness, it's just too late unless MSFT is willing to re-write all FCL and as such
probably break all existing applications.


Willy.
 
Don't forget that .NET (the CLI) is a multi language platform, imposing const correctness in
one language makes no sense if it's not enforced by all languages available for the
platform, more, it makes little sense if not enforced by the CLR.

Yes, we got into that in the VB newsgroups. Perhaps that's why const
never made it in.
Even C++ (the single language) doesn't enforce, const-correctness, it even provides a
facility to cast-away const.

I discussed this, as well. No, you cannot cast away const. Not
directly. That's an important point.

Yes, it's possible to access and write over something that is const,
as you can with any language that lets you use pointers. So, it can
be 'cast-away' indirectly, but I don't think this should be called a
'cast', since it very much indirect.

The important distinction, as I went into detail in VB, is the
compiler checks that 'const' brings. The fact that it isn't a 100%
solution to prevention of variable changing is NOT why it is so
valuable.

It is valuable because, as most C# programmers should appreciate, for
the same reason that C# forces good programming ethics.
Even if C# and VB were the only languages on .NET, it was simply not possible to enforce
const correctness, it's just too late unless MSFT is willing to re-write all FCL and as such
probably break all existing applications.

It is unforunate given what it can bring to writing robust code. It
is also unfortunate that people who haven't experienced it cannot
truly appreciate it. It would fit 100% into C#'s design, and effort
to force good code.

I fear I am starting the same conversation I had a few weeks ago.

Zytan
 
Yes, we got into that in the VB newsgroups. Perhaps that's why const
never made it in.


I discussed this, as well. No, you cannot cast away const. Not
directly. That's an important point.

Yes, it's possible to access and write over something that is const,
as you can with any language that lets you use pointers. So, it can
be 'cast-away' indirectly, but I don't think this should be called a
'cast', since it very much indirect.

The important distinction, as I went into detail in VB, is the
compiler checks that 'const' brings. The fact that it isn't a 100%
solution to prevention of variable changing is NOT why it is so
valuable.

It is valuable because, as most C# programmers should appreciate, for
the same reason that C# forces good programming ethics.


It is unforunate given what it can bring to writing robust code. It
is also unfortunate that people who haven't experienced it cannot
truly appreciate it. It would fit 100% into C#'s design, and effort
to force good code.

I fear I am starting the same conversation I had a few weeks ago.

This question came up at PDC'05, I believe, and if I recall the
response from the C# team was that const didn't make it into the
language because they were trying to figure out how to make it a 100%
solution, without providing the ability to cast it away, and without
ending up with the ugly cascading-cost problems that come up in C++
from time to time. Since they couldn't figure out how to do that, they
didn't include it in the language. Now it's probably too late. Too
bad, really.
 
Some where I read (may be "FAQ in C++"), even in C++ this kind of
local static variable is not safe for multi threaded applications. So,
better use functionoid.

That's very true. Of course, it would be true of any data referenced by a
function that may be executed by multiple threads. I suppose one other
argument (in addition to the ones I mention in my other post) is that in
other cases, it should be more clear to the programmer that the use of the
data is not thread-safe, whereas a static local may not be readily
apparent to a programmer as not being thread-safe.

And of course, my proposed alternate solution (using a parameter to the
function rather than a static local) solves the thread safety problem, by
creating a new copy of the data for each iteration of the function (and
thus also for each thread).

Pete
 
Now, how would you then write something like this in C#?

void f()
{
static int count;
if ( count > 0 )
Debug.Writeline( "recursion level" + count.ToString() );
count++;

// -- the internals of f

count--
}

Well, I don't know how you would write it. But I would write it like this:

void f(int count)
{
if (count > 0)
{
Debug.WriteLine("recursion level " + count);
}

// -- the internals of f, which presumably include another
// call to f, such as:
f(count + 1);
}

If you have some objection to changing the callers of f(), then just
create a stub:

void f()
{
f(0);
}

and create a new function:

void f(int count)
{
f(count + 1);
// the rest of the function remains unchanged
}

In other words, just do the usual thing to overload the function that you
would in C#.
IMHO, count is only interesting within the scope of f, and therefore
should be defined in f, and nowhere else. But C# forces me to define such
variables at class level, which is stupid.

See above. C# makes no such demand on you. It's true that it doesn't
allow for static local variables, but there are solutions other than
defining variables at the class level. Furthermore, I see very little
difference (if you are simply 100% against changing the function's
parameter list) between this:

void f()
{
static int count = 0;
}

and this:

static int count = 0;
void f()
{
}

It's true that the scope is different, but you're already breaking basic
laws of good coding practices by creating the static variable in the first
place. Just don't refer to the class-level member variable in other code,
if you feel that doing so would be a problem.
(OK, its only a minor nuisance, and other things are much worse. But
sometimes I wonder how language designers come to their results).

I too. I'm not really sure as to why the C# designers excluded static
local variables from the language. However, I do know that static locals
have very little practical use and are often abused (the biggest problem
is that when used, they can easily lead to code that is harder to
maintain, because the function behavior is not deterministic in a readily
apparent way...sort of a "kissing cousin" to code with side-effects, which
is another bad practice).

Another possibility is that static locals break the usual variable
initialization paradigm. That is, normally a variable declared within a
block is always initialized when that block of code is entered. Static
variables are initialized globally, only once. A common enough bug is a
variable declared locally as static, but which the programmer thinks is
initialized each time the block of code is entered. Getting rid of static
locals helps preserve the uniform behavior of variable declarations in C#
and helps avoid bugs where a programmer makes the wrong assumption about
when or how a variable is initialized.

Perhaps one or more of these reasons is related to why C# doesn't have
static locals, perhaps none are. I don't know. What I do know is that
the lack of static locals doesn't represent any sort of significant
impediment to writing good code, and I don't agree that it forces a
programmer to do anything that could be characterized as "stupid". If
anything, it tends to discourage programmers from doing something stupid..

Pete
 
Zytan said:
Yes, we got into that in the VB newsgroups. Perhaps that's why const
never made it in.


I discussed this, as well. No, you cannot cast away const. Not
directly. That's an important point.


Not sure what you call directly, but this...
....
void sqrval(const int &val)
{
const_cast<int &> (val) = val * val; // cast away const on val
}
int main()
{
int x = 22;
sqrval(x);
cout << "x after call: " << x << endl;
return 0;
}
prints:
x after call: 484

.... is pretty direct for me!
Yes, it's possible to access and write over something that is const,
as you can with any language that lets you use pointers. So, it can
be 'cast-away' indirectly, but I don't think this should be called a
'cast', since it very much indirect.

Name it as you like, the const_cast operator is meant to cast-away const.
The important distinction, as I went into detail in VB, is the
compiler checks that 'const' brings. The fact that it isn't a 100%
solution to prevention of variable changing is NOT why it is so
valuable.

I didn't say it would be invaluable, it's simply not that valuable as it could be if it was
enforced by the run-time.
But as I said previously, the run-time can not enforce it because this would require all
major languages to support const, something which is not feasible as some languages pre-date
..NET.
So what's left is support it at the language level, something which is done in C++/CLI, but
this offers nothing more than what you have in C++ , you got even less as the FCL which is
KEY in .NET doesn't supports const , and other .NET languages know nothing about const.

Consider following sample.

// Compile with: cl /clr:safe filename.cpp
//
using namespace System;
public ref class B
{
public:
B() : i(0) {}
internal:
int i;
};
public ref class C
{
public:
int i;
void ChangeInt(const B^ b)
{
B^ myb = const_cast<B^>(b); // fool the compiler and cast away const
myb->i = 10 ; // modify i
Foo(myb); // do whatever you please with b
}
};
int main()
{
B^ b = gcnew B();
C c;
c.ChangeInt(b);
Console::WriteLine(b->i);
}

see: because it's not enforceable on any parameter subsequently passed to a method (think
the FCL, written in C# and C++/CLI), it is not possible to insure the transitive closure on
the constness of the call chain for each parameter.
It is valuable because, as most C# programmers should appreciate, for
the same reason that C# forces good programming ethics.
See above.
It is unforunate given what it can bring to writing robust code. It
is also unfortunate that people who haven't experienced it cannot
truly appreciate it. It would fit 100% into C#'s design, and effort
to force good code.

Yep, but as I said the CLI is a multilanguage platform, which is much more valuable than
non-enforced constness.
I fear I am starting the same conversation I had a few weeks ago.

As I said, it's a can of worms, and IMO it makes no sense to open it any further as (IMO) we
won't have it anyway.

Willy.
 
Am Wed, 28 Feb 2007 18:03:08 +0100 schrieb Willy Denoyette [MVP]:
How is it different?

Argh! You know what the difference is!
IMHO it is undisputed good programming style to limit the visibility of an
object to the smallest possible area. If count is meaningful only in the
scope of a function, it should be possible to limit its visibility to
exactly that scope.

Local statics are the means to accomplish that.

Local statics might expose some properties that make them unsuitable for
certain tasks - they are not thread safe, for example (at least in C++).
They are useful in many situations, though. I gave a very simple example, I
can give others if somebody is interested.

[snip]
Because the C# language designers did not see a great value in it?
Well, seems questionable to me. I can see no disadvantage in having them.


Paule
 
Const is "Godsend"? IMO, on the contrary.
And thats why Anders Hejlsberg did not want't to introduce it so widely in
C#.
And after 8 yrs of C/C++, I really do agree with him.

The following is from an interview
(http://www.artima.com/intv/choicesP.html):


"
Anders: ... With respect to const, it's interesting, because we hear that
complaint all the time too: "Why don't you have const?" Implicit in the
question is, "Why don't you have const that is enforced by the runtime?"
That's really what people are asking, although they don't come out and say
it that way.

The reason that const works in C++ is because you can cast it away. If you
couldn't cast it away, then your world would suck. If you declare a method
that takes a const Bla, you could pass it a non-const Bla. But if it's the
other way around you can't. If you declare a method that takes a non-const
Bla, you can't pass it a const Bla. So now you're stuck. So you gradually
need a const version of everything that isn't const, and you end up with a
shadow world. In C++ you get away with it, because as with anything in C++
it is purely optional whether you want this check or not. You can just whack
the constness away if you don't like it.
"
 
Am Thu, 01 Mar 2007 15:10:40 +0800 schrieb Peter Duniho:
Well, I don't know how you would write it. But I would write it like this:

void f(int count)
{
if (count > 0)
{
Debug.WriteLine("recursion level " + count);
}

// -- the internals of f, which presumably include another
// call to f, such as:
f(count + 1);
}

Well, I *definitely* do not want this. No No No!

How could you ever assume that f will be called directly in f? My original
example does not have such an assumption. To be honest: In a real world
application, f might call h, which might call g, which invokes a delegate
that sends a Windows-message which leads to another call of f - sometimes,
at least. But sometimes not. For debugging purposes I want to hit a
breakpoint if that ever happens. You are not advising me to give all these
functions an additional parameter? LOL.

[snip]
See above. C# makes no such demand on you. It's true that it doesn't
allow for static local variables, but there are solutions other than
defining variables at the class level.

And these are?

Your example above tries to show how we can solve the given task of
"recursion detection". It can work in certain situations (e.g. recursion
does not go through Windows messages & message pump). It has the
disadvantage that all functions in the chain ( f, g, h, delegate etc.) now
must have an additional parameter. What if h is used in another context,
where recursion is not an issue? what would the caller of h provide for the
value? You impose additional complexity on h and all clients of h simply
because f has some problem? No, sire.

See the problem? The recursion-checking-value has absolutely no meaning for
h or anybody else except f. And thats why it should be confined to f.


Furthermore, I see very little
difference (if you are simply 100% against changing the function's
parameter list) between this:

void f()
{
static int count = 0;
}

and this:

static int count = 0;
void f()
{
}

It's true that the scope is different, but you're already breaking basic
laws of good coding practices by creating the static variable in the first
place.

Could you please elaborate a little why use of static variables is bad
coding practice? I can't see this.

Just don't refer to the class-level member variable in other code,
if you feel that doing so would be a problem.

Shudder. Then why do we need "private" at all? Simply don't refer to class
members that you feel should not be accessed from outside the class. You
can write some documentation for the user of your class that this or that
field is not to be used from client code. Yes! Back to the basics! Make the
language easier!

[snip]
However, I do know that static locals
have very little practical use

Well, depends. I gave a very basic example for their use, and I can give
some more, which are more elaborate. Of course, if you never used them you
might not miss them. But from a greater distance the picture is different.
Again, when you think you don't need local statics: please give a suitable
implementation of the recursion detection problem.

and are often abused (the biggest problem
is that when used, they can easily lead to code that is harder to
maintain, because the function behavior is not deterministic in a readily
apparent way...sort of a "kissing cousin" to code with side-effects, which
is another bad practice).

Harder to maintain? Not really. Side effects? You should give an example,
how a static local variable would allow for any side effect.
Another possibility is that static locals break the usual variable
initialization paradigm. That is, normally a variable declared within a
block is always initialized when that block of code is entered. Static
variables are initialized globally, only once.

Yes. But same argumentation holds for statics at class level. Non-statics
are initialized in the ctor, statics are not. Same thing, different place.
A common enough bug is a
variable declared locally as static, but which the programmer thinks is
initialized each time the block of code is entered.

Well, if the programmer does not know the rulez, I can't help. My advice
then is simply not to use language features you don't fully understand. A
much more complex topic are delegates or generics ot anonymous functions -
do you suggest to delete them from the language, too?


Getting rid of static
locals helps preserve the uniform behavior of variable declarations in C#
and helps avoid bugs where a programmer makes the wrong assumption about
when or how a variable is initialized.

Look at class level. There is no "uniform behavior" of variables. statics
are different bests that non statics. If this is too difficult - don't use
statics. Period.

[dnip]
the lack of static locals doesn't represent any sort of significant
impediment to writing good code, and I don't agree that it forces a
programmer to do anything that could be characterized as "stupid".


Well, then give an implementation of the "recursion detection problem" in
C# that is acceptable.

Paule
 
Paul Werkowitz said:
Am Wed, 28 Feb 2007 18:03:08 +0100 schrieb Willy Denoyette [MVP]:
How is it different?

Argh! You know what the difference is!
IMHO it is undisputed good programming style to limit the visibility of an
object to the smallest possible area. If count is meaningful only in the
scope of a function, it should be possible to limit its visibility to
exactly that scope.

Local statics are the means to accomplish that.

Local statics might expose some properties that make them unsuitable for
certain tasks - they are not thread safe, for example (at least in C++).
They are useful in many situations, though. I gave a very simple example, I
can give others if somebody is interested.

[snip]
Because the C# language designers did not see a great value in it?
Well, seems questionable to me. I can see no disadvantage in having them.


Paule
 
IMHO it is undisputed good programming style to limit the visibility of an
object to the smallest possible area. If count is meaningful only in the
scope of a function, it should be possible to limit its visibility to
exactly that scope.

Local statics are the means to accomplish that.

I completely agree. You can't argue against this.
Local statics might expose some properties that make them unsuitable for
certain tasks - they are not thread safe, for example (at least in C++).
They are useful in many situations, though. I gave a very simple example, I
can give others if somebody is interested.

The recursion example is the only one I have used (other than for
quick debuggin test code), so maybe it's not useful enough for the C#
to warrant its inclusion?

Zytan
 
IMHO, count is only interesting within the scope of f, and therefore
See above. C# makes no such demand on you. It's true that it doesn't
allow for static local variables, but there are solutions other than
defining variables at the class level.

Yes, they may not be as fast, but they exist.
Furthermore, I see very little
difference (if you are simply 100% against changing the function's
parameter list) between this:

void f()
{
static int count = 0;
}

and this:

static int count = 0;
void f()
{
}

I do. You letting it be visible to *far* more than it should be
visible to. The difference is just as grand relatively speaking as
changing it from class scope to global scope.
It's true that the scope is different, but you're already breaking basic
laws of good coding practices by creating the static variable in the first
place.

What good coding practice is being broken? I don't follow.

Also, evem if it is breaking a law of good coding practice (which I
can't see ATM), it doesn't mean you should further break another law.
Just don't refer to the class-level member variable in other code,
if you feel that doing so would be a problem.

That's similar to saying 'just don't access the global variable where
you shouldn't be, if you feel that would be a problem'. Or, similar
to: 'just don't change the value of the variable if you want it to be
constant'. These are just cop-outs. You can't claim the
rightiousness of good coding practices forced by the language are a
good thing, and then turn 180 and say 'well, that's ok that the
language doesn't enforce that, because you can enforce it yourself'.
It's all the same soup.
I too. I'm not really sure as to why the C# designers excluded static
local variables from the language.

I thought maybe it had to do with type/reference variables? But they
wouldn't be a problem, would they, if static local vars existed?
However, I do know that static locals
have very little practical use and are often abused (the biggest problem
is that when used, they can easily lead to code that is harder to
maintain, because the function behavior is not deterministic in a readily
apparent way...sort of a "kissing cousin" to code with side-effects, which
is another bad practice).

Ah, now this makes sense. I agree. Kind of like the token searching
string function in C, which depends on the last call for how it reacts
the second and third times. Its funky that way. And we have local
statics to blame.

And it is true they have little practical use (little, not none).

So, I can see why they got rid of it. The time was better spent
elsewhere. After all, C# wasn't meant to run at speeds with C, so the
fact that local statics can improve speed (by negating the need of
parameter passing) is reduced. Good coding practices is a much higher
concern and C# does a great job.

I never really ran into abuse of local statics, so I didn't consider
this. Thanks.
Another possibility is that static locals break the usual variable
initialization paradigm. That is, normally a variable declared within a
block is always initialized when that block of code is entered. Static
variables are initialized globally, only once. A common enough bug is a
variable declared locally as static, but which the programmer thinks is
initialized each time the block of code is entered. Getting rid of static
locals helps preserve the uniform behavior of variable declarations in C#
and helps avoid bugs where a programmer makes the wrong assumption about
when or how a variable is initialized.

True. Note that static c'tors are initialized in the same manner, so
the issues above still hold true for them. But, the issues are *well*
worth having static c'tors!
Perhaps one or more of these reasons is related to why C# doesn't have
static locals, perhaps none are. I don't know. What I do know is that
the lack of static locals doesn't represent any sort of significant
impediment to writing good code, and I don't agree that it forces a
programmer to do anything that could be characterized as "stupid". If
anything, it tends to discourage programmers from doing something stupid.

Well, if you replace them with class vars, then that's "stupid" (and
watch it, as smart as you are, you mentioned this alternative, so if I
am right that it is not a proper alternative, C#'s limitations just
made you suggest 'bad code'). If you replace them with parameter
passing, you get slower code, but that's not "stupid", although some
people may complain about the speed, but then again, C# wasn't meant
for speed, so it's ok. The benefit of good code, the purpose of C#,
far outweights the slower speed of parameter passing, so they made the
right choice.

I still miss them, though.

Zytan
 
This question came up at PDC'05, I believe, and if I recall the
response from the C# team was that const didn't make it into the
language because they were trying to figure out how to make it a 100%
solution, without providing the ability to cast it away, and without
ending up with the ugly cascading-cost problems that come up in C++
from time to time. Since they couldn't figure out how to do that, they
didn't include it in the language. Now it's probably too late. Too
bad, really.

I didn't think the lack of ability to absolutely prevent const casting
away is an issue. Just because 0.1% of the population can pick locks,
does it mean you shouldn't use locks?

Zytan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top