I'm practicing on setters and getters, got this error message:
Cannot assign to 'GetnewName' because it is a'method group'
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Program
{
public static void Main(string[] args)
{
Bird b = new Bird();
b.GetnewName = "Tweety";
b.Chirp();
Bird b2 = new Bird();
b2.GetnewName = "Woody";
b2.Chirp();
}
}
public class Bird
{
private string name;
private double weight = 30.5d;
public void SetName(string newName)
{
if (newName != null && newName.Length > 2)
{
System.Console.WriteLine("Bird already has a name");
this.name = newName;
}
else if (newName.Length < 3)
{
System.Console.WriteLine("New name must be longer than two chars");
}
else
{
name = newName;
}
}
public string GetnewName()
{
return this.name;
}
public void Chirp()
{
System.Console.WriteLine(name + " says chirp!");
}
}
}
You need to set name using the SetName method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Program
{
public static void Main(string[] args)
{
Bird b = new Bird();
b.SetName("Tweety");
b.Chirp();
Bird b2 = new Bird();
b2.SetName("Woody");
b2.Chirp();
Console.ReadLine();
}
}
public class Bird
{
private string name;
private double weight = 30.5d;
public void SetName(string newName)
{
if (newName != null && newName.Length > 2)
{
System.Console.WriteLine("Bird already has a name");
this.name = newName;
}
else if (newName.Length < 3)
{
System.Console.WriteLine("New name must be longer than two chars");
}
else
{
name = newName;
}
}
public string GetnewName()
{
return this.name;
}
public void Chirp()
{
System.Console.WriteLine(name + " says chirp!");
}
}
}
You are using GetnewName as if it were a property, but you defined it as a pair of methods.
Property syntax combines the getter and the setter under a single name. Then C# re-routs assignments to the setter, and reads to the getter method:
public string Name {
get => name
set {
if (value != null && value.Length > 2) {
System.Console.WriteLine("Bird already has a name");
name = value;
} else if (value.Length < 3) {
System.Console.WriteLine("New name must be longer than two chars");
} else {
name = value;
}
}
}
Note: get => name above uses the new syntax. Old syntax for the same was get { return name; }
You cannot assign a string to a method, this causes the error.
I c# we don't use getters and setters as in Java or C++. Instead we have properties which we use. You can assign and read values as if they where fields but they have special methods called accessors. You should refactor your class like this:
public class Bird
{
private string name;
private double weight = 30.5d;
public string Name
{
get => name;
set
{
if (value != null && value.Length > 2)
{
Console.WriteLine("Bird already has a name");
name = value;
}
else if (value != null && value.Length < 3)
{
Console.WriteLine("New name must be longer than two chars");
}
else
{
name = value;
}
}
}
public void Chirp()
{
System.Console.WriteLine(name + " says chirp!");
}
}
Then you can use it as:
var bird = new Bird();
// assign a value (equivalent to SetName method in your original code)
bird.Name = "Woody";
// read a value (equivalent to GetName method in your original code)
Console.WriteLine(bird.Name);
It seems like you want this:
public string Name
{
get; private set;
}
public void SetName(string newName)
{
if (newName != null && newName.Length > 2)
{
System.Console.WriteLine("Bird already has a name");
Name = newName;
}
else if (newName.Length < 3)
{
System.Console.WriteLine("New name must be longer than two chars");
}
else
{
Name = newName;
}
}
The get; will automatically return the value of your property, without the need for a private backing property, and typically you could use the setter inside the property to set the value, but since you need to pass a parameter too it, it seems like making the setter private and creating a separate method to actually set it would suit your needs.
Related
I am trying to use a while loop when using a public static class method.
I don't know where to break the loops (while and foreach).
the target is, prompting the user to enter a correct format of his name which contains all letters.
namespace Project1
{
public class Student
{
public int id;
public string name;
public string familyName;
public int age;
public static int numberOfStudents;
public static void IsAllLetter(string name)
{
while (true)
{
foreach (char c in name)
{
if (!char.IsLetter(c) || name == null)
{
break;
}
}
Console.WriteLine("name and family name must contain letters");
Console.WriteLine("please try again");
}
}
}
public class Program
{
public static void Main(string[] args)
{
// get the name
Student student1 = new Student();
Console.WriteLine("name of the student?");
student1.name = Console.ReadLine();
Student.IsAllLetter(student1.name);
}
}
}
Let's extract method, name validation:
using System.Linq;
...
// Simplest, some names like
// "Charles de Batz de Castelmore d'Artagnan"
// does not pass
private static bool IsValidName(string value) {
return
!string.IsNullOrEmpty(value) && // value can't be null or ""
value.All(letter => char.IsLetter(letter)); // All letters validation
}
Then we can implement a name input:
private static string ReadName(string title) {
while (true) {
if (!string.IsNullOrEmpty(title))
Console.WriteLine(title);
// .Trim() - let be nice and tolerate leading / trailing whitespaces
string name = Console.ReadLine().Trim();
if (IsValidName(name))
return name;
Console.WriteLine("Sorry, the name is not valid. Please, try again");
}
}
Finally, you can just use these methods in your business logic without delving into validation details:
Student student1 = new Student();
student1.name = ReadName("name of the student?");
You need only a single loop that iterates the characters in your string and return false if any "wrong" character was found:
public static bool IsAllLetter(string name)
{
if(string.IsNullOrEmpty(name))
return false;
foreach (char c in name)
{
if (!char.IsLetter(c) || name == null)
{
return false;
}
}
return true;
}
Than call that method in a loop:
string name;
while(true)
{
Console.WriteLine("name of the student?");
name = Console.ReadLine();
if(IsAllLetter(name) break;
}
Now you have a name which you can assign to your Student:
var s = new Student { Name = name };
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Crystal_Message
{
class Message
{
private int messageID;
private string message;
private ConcurrentBag <Employee> messageFor;
private Person messageFrom;
private string calltype;
public Message(int iden,string message, Person messageFrom, string calltype, string telephone)
{
this.messageID = iden;
this.messageFor = new ConcurrentBag<Employee>();
this.Note = message;
this.MessageFrom = messageFrom;
this.CallType = calltype;
}
public ConcurrentBag<Employee> ReturnMessageFor
{
get
{
return messageFor;
}
}
public int MessageIdentification
{
get { return this.messageID; }
private set
{
if(value == 0)
{
throw new ArgumentNullException("Must have Message ID");
}
this.messageID = value;
}
}
public string Note
{
get { return message; }
private set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Must Have a Message");
}
this.message = value;
}
}
public Person MessageFrom
{
get { return messageFrom; }
private set
{
this.messageFrom = value;
}
}
public string CallType
{
get { return this.calltype; }
private set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentNullException("Please specify call type");
}
this.calltype = value;
}
}
public void addEmployee(Employee add)
{
messageFor.Add(add);
}
public override string ToString()
{
return "Message: " + this.message + " From: " + this.messageFrom + " Call Type: " + this.calltype + " For: " + this.returnMessagefor();
}
private string returnMessagefor()
{
string generate="";
foreach(Employee view in messageFor)
{
generate += view.ToString() + " ";
}
return generate;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
Message testEquals = obj as Message;
if((System.Object)testEquals == null)
{
return false;
}
return (this.messageID == testEquals.messageID) && (this.message == testEquals.message) && (this.messageFor == testEquals.messageFor) && (this.messageFrom == testEquals.messageFrom) && (this.calltype == testEquals.calltype);
}
public bool Equals(Message p)
{
if ((Object)p == null)
{
return false;
}
return (this.messageID == p.messageID) && (this.message == p.message) && (this.messageFor == p.messageFor) && (this.messageFrom == p.messageFrom) && (this.calltype == p.calltype);
}
public override int GetHashCode()
{
unchecked
{
return this.messageID.GetHashCode() * 33 ^ this.message.GetHashCode() * 33 ^ this.messageFor.GetHashCode() * 33 ^ this.messageFrom.GetHashCode() * 33 ^ this.calltype.GetHashCode();
}
}
}
}
I have a Message class where a user could leave a message for more than one person. I have a getter for it, however, is returning a ConcurrentBag<> the way I've done proper practice? If not, how do i return the ConcurrentBag<> so I can loop through it and display it?
ConcurrentBag<T> is an IEnumerable<T>. You can loop through it as usual. However, as this is a thread safe collection, there are performance concerns to using it.
If you want to get rid of the performance impact while looping, call ToArray on it and return the new array instead.
public IEnumerable<Employee> ReturnMessageFor
{
get
{
return messageFor.ToArray();
}
}
It's not clear to me what you are trying to accomplish.
Are you trying to externalize the Bag for all operations? Because that's what you did...
If you want to externalize something you can iterate over you should either return the Bag as IEnumerable or return an array or a list copied from the Bag.
Either way it's safe to iterate over. Might not be the best in terms of performance, but that's another question.
// Option 1
public IEnumerable<Employee> ReturnMessageFor
{
get
{
return messageFor;
}
}
// Option 2
public Employee[] ReturnMessageFor
{
get
{
return messageFor.ToArray();
}
}
Notes:
You might want to make messageFor readonly (in the code you posted it is readonly).
Remember that a ConcurrentBag allows you to safely iterate over a snapshot of the collection in a thread safe manner, but it does not lock the items in the collection.
I want to limit my string, so that you have to put a minimum of 3 chars and a max of 10 chars in. Is this possible in the following code below?
main.cs:
class Program
{
static void Main(string[] args)
{
Something hello = new Something();
string myname;
Something test = new Something();
myname = Console.ReadLine();
test.Name = myname;
}
}
class with properties:
class Okay : IYes
{
private string thename;
public string Name
{
get {return thename;}
set {thename = value;} //what to put here???
}
}
The setter is probably not the best place to check. You should make the check at the point of input:
string myname = "";
while (myname.Length<3 || myname.Length >10)
{
Console.WriteLine("Please enter your name (between 3 and 10 characters");
myname = Console.ReadLine();
}
test.Name = myname;
Obviously you can take some steps to make this more user friendly: maybe a different message after the first failure, some way of getting out of the loop, etc.
Try this:-
public string Naam
{
get { return thename; }
set
{
if (value.Length >= 3 && value.Length <= 10)
thename = value;
else
throw new ArgumentOutOfRangeException();
}
}
class Okay : IYes
{
private string name;
public string Name
{
get { return name; }
set
{
if (value == null) throw new ArgumentNullException("Name");
if (value.Length < 3 || value.Length > 10)
throw new ArgumentOutOfRangeException("Name");
name = value;
}
}
}
You can also truncate the string if it's too long, rather than throwing an exception by just taking (up to) the first 10 characters:
class Okay : IYes
{
private string name;
public string Name
{
get { return name; }
set
{
if (value == null) throw new ArgumentNullException("Name");
if (value.Length < 3) throw new ArgumentOutOfRangeException("Name");
name = string.Join("", value.Take(10));
}
}
}
private static void GenericTester()
{
Okay ok = new Okay {Name = "thisIsLongerThan10Characters"};
Console.WriteLine(ok.Name);
}
// Output:
// thisIsLong
I've got something like this in my property/accessor method of a constructor for my program.
using System;
namespace BusinessTrips
{
public class Expense
{
private string paymentMethod;
public Expense()
{
}
public Expense(string pmtMthd)
{
paymentMethod = pmtMthd;
}
//This is where things get problematic
public string PaymentMethod
{
get
{
return paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(" "))
paymentMethod = "~~unspecified~~";
else paymentMethod = value;
}
}
}
}
When a new attribute is entered, for PaymentMethod, which is null or a space, this clearly does not work. Any ideas?
do you perhaps just need to replace string.IsNullOrWhiteSpace(" ") with string.IsNullOrWhiteSpace(value) ?
From your posted code, you need to call:
this.PaymentMethod = pmtMthd;
instead of
paymentMethod = pmtMthd;
The capital p will use your property instead of the string directly. This is why it's a good idea to use this. when accessing class variables. In this case, it's the capital not the this. that makes the difference, but I'd get into the habit of using this.
Jean-Barnard Pellerin's answer is correct.
But here is the full code, which I tested in LinqPad to show that it works.
public class Foo {
private string _paymentMethod = "~~unspecified~~";
public string PaymentMethod
{
get
{
return _paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(value))
_paymentMethod = "~~unspecified~~";
else _paymentMethod = value;
}
}
}
With a main of:
void Main()
{
var f = new Foo();
f.PaymentMethod = "";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = " ";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = "FooBar";
Console.WriteLine(f.PaymentMethod);
}
Output from console:
~~unspecified~~
~~unspecified~~
FooBar
How could I make this work?:
public class myClass
{
public string first;
public int second;
public string third;
}
public string tester(object param)
{
//Catch the name of what was passed not the value and return it
}
//So:
myClass mC = new myClass();
mC.first = "ok";
mC.second = 12;
mC.third = "ko";
//then would return its type from definition :
tester(mC.first) // would return : "mc.first" or "myClass.first" or "first"
//and
tester(mC.second) // would return : "mc.second" or "myClass.second" or "second"
In the absence of infoof, the best you can do is Tester(() => mC.first) via expression trees...
using System;
using System.Linq.Expressions;
public static class Test
{
static void Main()
{
//So:
myClass mC = new myClass();
mC.first = "ok";
mC.second = 12;
mC.third = "ko";
//then would return its type from definition :
Tester(() => mC.first); // writes "mC.first = ok"
//and
Tester(() => mC.second); // writes "mC.second = 12"
}
static string GetName(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess)
{
var me = (MemberExpression)expr;
string name = me.Member.Name, subExpr = GetName(me.Expression);
return string.IsNullOrEmpty(subExpr)
? name : (subExpr + "." + name);
}
return "";
}
public static void Tester<TValue>(
Expression<Func<TValue>> selector)
{
TValue value = selector.Compile()();
string name = GetName(selector.Body);
Console.WriteLine(name + " = " + value);
}
}
This is not possible. Variable names don't exist in compiled code, so there's no way you can retrieve a variable name at runtime
That's not possible. "param" will have no information on where the value came from.
When calling tester(), a copy of the value in one of the properties is made, so the "link" to the property is lost.