Main class USER and subclass PLAYER, how to PLAYER=USER ? c# - c#

I'm making the first server-client application and i need your help.
The client must autenticate itself and after it can play some games.
So I've got USER and PLAYER classes: USER comprends all user registered and PLAYER comprends user that play game (there are PLAYER_GAME1,PLAYER_GAME2, etc..).
Inside USER i've proprieties like name, surname, id and etc.
Inside PLAYER i need have the proprieties of the user plus point, time in game, etc.
Actually:
public class USER
{
public string name, surname, ID, password, IP;
WALLET wallet;
bool login;
datetime lastAccess;
TcpClient socket;
Thread receiveMessages;//this receive message for log-in
...*other 30 proprieties*
public USER(string n,string s,string _id,string pw)
{
*inizialize all variables*
}
}
public class PLAYER
{
public USER user;
Thread receiveMessages;
int points;
bool online;
...*other proprieties*
public PLAYER(USER u)
{
user=u;
*inizialize all variables*
}
}
so for getting name i have to do:
PLAYER p= new PLAYER(new USER(...));
string name=p.user.name;
I think is more smart make PLAYER subclass of USER and when user want play games i "expand" the class user with the proprieties of player so i need do :
public class USER
{
protected string name, surname, ID, password, IP;
WALLET wallet;
bool login;
datetime lastAccess;
TcpClient socket;
Thread receiveMessages;//this receive message for meneage the game
...*other 30 proprieties*
public USER(string n,string s,string _id,string pw)
{
*inizialize all variables*
}
}
public class PLAYER : USER
{
Thread receiveMessages;
...*other proprieties*
public PLAYER():base()// here, what could i do?
{
*inizialize all PLAYER variables*
}
}
so for getting name i would do:
PLAYER p= new PLAYER();
p=exixtingUser;
string name=p.name;
I know that SUBCLASS=MAINCLASS is impossible so how could i do it?

As I can understand, you want to create a Player role based on the User information.
If our classes are:
public class User
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public User(Guid id, string name)
{
Id = id;
Name = name;
}
}
public class Player : User
{
public TimeSpan TimeInGame { get; set; }
public Player(User userInfo)
: base(userInfo.Id, userInfo.Name)
{
}
}
usage of such constructor will be:
var player = new Player(user);
You can use a constructor accepting the User information. You also can write the extension method .ToPlayer() or something like it.
I think you should read the article about inheritance on the MSDN and continue the reading with Constructors articles.
Update:
I've understood your problem, but, unfortunately, there is no easy solution for it. You can either remain on your current solution, this will be alright if your application creates many players based on one user. If it is a -one-to-one relation, you can do something like this:
public class User
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public User(User copyFrom)
{
Id = copyFrom.Id;
Name = copyFrom.Name;
// copy all the fields you need
}
}
public class Player : User
{
public TimeSpan TimeInGame { get; set; }
public Player(User userInfo)
: base(userInfo)
{
}
}
The main problem of this solution is that you have to copy it by yourself, without any automation mechanism. You can find a similar question here:
Copying the contents of a base class from a derived class

Per your request of clarification, and to only offer expansion of the nice solution by VMAtm.
public class USER
{
// changed to public getters so externally can be accessed
// for read-only, but write access for USER instance or derived class (PLAYER)
// QUESTIONABLE on public accessor on password
public string name { get; protected set; }
public string surname { get; protected set; }
public string ID { get; protected set; }
public string password { get; protected set; }
public string IP { get; protected set; }
WALLET wallet;
bool login;
datetime lastAccess;
TcpClient socket;
// make this too protected AND available to player AND any others that make sense above
protected Thread receiveMessages; //this receive message for meneage the game
...*other 30 proprieties*
public USER(string nm, string sur, string _id, string pw)
{
name = nm;
surname = sur;
ID = _id;
password = pw;
InitUserVars();
}
private InitUserVars()
{
*inizialize all variables*
}
}
public class PLAYER : USER
{
// removed the Thread receiveMessages;
// as it is avail on parent class USER
...*other proprieties*
// slightly different option, instead, call the "THIS" version of constructor
public PLAYER(USER ui) : this(ui.name, ui.surname, ui.ID, ui.password )
{ }
// Now, pass same initializing parms to the USER base class so that all still runs the same.
public PLAYER(string nm, string sur, string _id, string pw) : base(nm, sur, _id, pw)
{
// by calling the "base" above, all default behaviors are handled first
// Now, we can do whatever else specific to the PLAYER
InitPlayerVars()
}
private InitPlayerVars()
{
*inizialize variables associated with PLAYER that is NOT part of USER baseclass*
}
}
So now, with the same parameter constructor arguments in the player expanded from the user, you can actually jump to creating a player directly with parms or indirectly by-passing a user via.
PLAYER p1 = new PLAYER( "name", "mr", 123, "mypassword" );
or as result using an INSTANCE of a USER
USER u1 = new USER( "name", "mr", 123, "mypassword" );
PLAYER p1 = new PLAYER(u1);
Does this help clarify some initialization of multiple class implementations for you?

Related

Create a class to store classes that use different interfaces

I am working on a project in C# which needs to store information about a user. This information is retrieved from their respective libraries which have their own implementations and interfaces. Currently users can either connect from Twitch or Discord, so they will be represented as either TwitchUser or DiscordUser objects. However, I would like to write a class User that will contain one of the 2 classes which should make it easier for me to refer to them in code.
Here is an example of how TwitchUser and DiscordUser look like:
public class TwitchUser : IChatUser
{
public TwitchUser();
public string Id { get; }
public string UserName { get; }
public string DisplayName { get; }
public string Color { get; }
//and so on...
}
public class DiscordUser : SnowflakeObject, IEquatable<DiscordUser>
{
public virtual string Email { get; internal set; }
public virtual string Username { get; internal set; }
public bool Equals(DiscordUser e);
//and so on...
}
I thought I could create a class which accepts generic types but with my implementation I would have to pass BOTH classes which means one of them would be null. This doesn't feel right to me.
public class User<T,D>
{
public TwitchUser VarA { get; set; }
public DiscordUser VarB { get; set; }
}
What is the correct way of combining 2 classes that have different implementations and don't have interfaces in common between?
I would then also write code inside my User class to return user IDs etc.
Update
For example, when I request a user ID I would perform a check inside User whether I stored a TwitchUser or DiscordUser, and based on the outcome I would return an attribute which represents the user's ID on that platform.
Update 2
A user may only be represented by one of the two classes. For example, if they used Discord as their platform to login then they will only have a DiscordUser object associated with them. Same applies to Twitch platform and it using TwitchUser object. The reason they are different is because these implementation were written by different people using 2 different libraries, which is why they don't use the same interfaces, or inherit from one common class. So what I am trying to do is retroactively add some sort of an inheritance here. This way, when I want to refer to the user in my code, I don't need to write 2 overloads for a function (where one uses TwitchUser and the other uses DiscordUser). I just want to be able to refer to User and let that class decide for me.
those 2 clases I mentioned cannot be modified [...] I want to be able to access all the necessary attributes stored in DiscordUser and TwitchUser
That sounds like a good fit for the adapter pattern. Define an interface as common denominator:
public interface IUser
{
string Id { get; }
string Username { get; }
}
Note that this interface can only contain properties that both objects share, or you're going to need null checks all over the place.
Then create an adapter for each type you want to wrap:
public class TwitchUserAdapter : IUser
{
private readonly TwitchUser _user;
public TwitchUserAdapter(TwitchUser user)
{
_user = user;
}
public string Id => _user.Id;
public string Username => _user.UserName;
}
public class DiscordUserAdapter : IUser
{
private readonly DiscordUser _user;
public TwitchUserAdapter(DiscordUser user)
{
_user = user;
}
public string Id => _user.Id;
public string Username => _user.Username;
}
Now you can treat both the same:
var users = new List<IUser>();
users.Add(new TwitchUserAdapter(new TwitchUser { Id = "Tfoo", UserName = "Tbar" }));
users.Add(new DiscordUserAdapter(new DiscordUser { Id = "Dfoo", Username = "Dbar" }));
foreach (var user in users)
{
Console.WriteLine($"Id: {user.Id}, Name: {user.Username}");
}
"What is the correct way of combining 2 classes that have different implementations and don't have interfaces in common between?"
One way to accomplish this might be to create a class that contains all the properties you care about, and then create static methods that return an instance of the class based on the properties of a TwitchUser or a DiscordUser:
public class User
{
// Add just the properties you care about
public string Id { get; private set; }
public string UserName { get; private set; }
public string DisplayName { get; private set; }
public string Color { get; private set; }
public string Email { get; private set; }
// Make the default constructor private so instances of this
// class can only be created from one of our static methods
private User() { }
public static User FromTwitch(TwitchUser twitchUser)
{
return new User
{
Id = twitchUser.Id,
Color = twitchUser.Color,
DisplayName = twitchUser.DisplayName,
UserName = twitchUser.UserName
};
}
public static User FromDiscord(DiscordUser discordUser)
{
return new User
{
Email = discordUser.Email,
UserName = discordUser.Username
};
}
}

Implementing a simple achievement system for dotnet mvc

I'm working on creating a banking application for a school project that uses ASP.net MVC 4. As part of the application we have a set of achievements for the user. When the User completes an achievement, say "Create a Savings Account" they should be rewarded by unlocking the achievement and sending a notification. Here's the Achievement Class:
public class Achievement
{
public string UserID { get; set; }
public AchievementType AchType { get; set; }
public bool Completed { get; set; }
public string Description { get; set; }
public int CountToUnlock { get; set; }
}
public enum AchievementType
{
CREATE_SAVINGS_ACCOUNT, CREATE_GOAL, COMPLETE_GOAL, ACCOUNT_5K,
ACCOUNT_10K, UPDATE_GOAL, ADD_TRANSACTION, SAVE_1K_TOTAL
};
The part I'm struggling with is how to make sure that every user has the same set of achievements and where to trigger those achievements. Whether they should be in an AchievementController or someplace else.
My initial reaction is that you should have a Customer class, which contains a List of that customer's achievements.
Where you trigger the adding of these achievements, depends upon where the logic lives that determines when they've reached one of the goals. For your school project, are you going to rig up a page to simulate this?
Some pseudo to get you started:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Achievement> Achievements { get; set; }
public bool AddAchievement(Achievement newAchievement)
{
// Check to ensure customer doesn't already have the new achievement
// Persist to the database
// Add to local collection
Achievements.Add(newAchievement);
// Send email to customer
// Anything else
// Return
return true;
}
}
Suggestion: Your enum's options should be ProperCase, and put one item on each line. Good to make them adequately descriptive too.
public enum AchievementType
{
CreatedSavingsAccount,
CreatedGoal,
CompletedGoal,
AccountBalance5000,
// [...]
}

Cannot pass an object in wcf

I'm currently working on my final project which contains the use of WCF, WPF and C# and I'm having a hard time transfering an object through wcf.
I get an error after a while which says that the server did not provide a meaningful response.
The classes that are in use in the method that crashes are:
[DataContract]
public class Player
{
//public static int clientID = 0;
[DataMember]
public int Wins { get; set; }
[DataMember]
public int Loses { get; set; }
[DataMember]
public int realID { get; }
[DataMember]
public string nickName { get; set; }
public Player(int Wins, int Loses, string nickName)
{
this.Wins = Wins;
this.Loses = Loses;
this.nickName = nickName;
//clientID++;
realID = 1; //clientID;
}
}
[DataContract]
public class Run
{
[DataMember]
public List<Player> Players { get; set; }
[DataMember]
public bool isActive { get; set; }
public Run()
{
Players = new List<Player>();
}
public void playerJoined(Player player)
{
Players.Add(player);
}
public void playerLeft(Player player)
{
if (Players.Contains(player)) Players.Remove(player);
}
public void generateRun()
{
// TODO: get a random map from the DB and pass it to all players
return;
}
}
and the method that crashes the code is:
public Run getRunDetails(int runNumber)
{
runNumber = runNumber - 1;
return Runs[runNumber];
}
the code at the client side is:
ListBoxItem tempItem = ((ListBoxItem)allRuns.SelectedItem);
if(tempItem != null && !tempItem.Content.Equals("There are no runs available, create one now."))
{
string numString = ((string)tempItem.Content);
numString = numString.Substring(4, numString.Length - 4);
run = Service.getRunDetails(int.Parse(numString));
}
After some time of debugging I've found out the problem is in the list variable, I've tried to change it only to a Player variable -> getting the same error. Same goes for making my buffer and message sizes bigger.
The only way the code wont crash and send my Run object is when the List is not a data member..
//[DataMember]
public List<Player> Players { get; set; }
If I do the above the code works perfectly but I desperately need the List passed to the client side.
Sorry for the long post but I don't have a very long time and I need it done, any help will be very appreciated.
(Also, sorry for the poor formatting, I did my best)
I'm pretty sure the problem here is that you don't have a parameterless constructor in your Player...
try to add a
public Player() {}
to your class...
Either that or because your 'realId' [DataMember] has no setter, see this link for tips on correctly serializing readonly members.
WCF: Exposing readonly DataMember properties without set?
Also, dont forget to 'Update Service Reference' on the WCF Service in the Visual Studio client Project if you have changed members in classes that are passed across the WCF Channel.

Model validation, database constraints

I'm trying to add some architecture to my projects and enrich my models.
I started with CQS (implementation similar to that one: CQS-Sample) and here's my first problem.
Let's say I have two classes like these below:
public class Network
{
public int Id { get; set; }
public User User { get; set; }
private IQueryFactory _queryFactory { get; set; }
public Network(IQueryFactory queryFactory)
{
_queryFactory = queryFactory;
}
public void AddUser(User user)
{
if(this.User == null && user != null)
{
userHasUniqueEmail(user);
this.User = user;
}
}
private void userHasUniqueEmail(User user)
{
bool isUnique = _queryFactory.ResolveQuery<INewUserUniqueQuery>().Execute(user.Email);
if (!isUnique)
{
throw new ArgumentException("E-mail is not unique");
}
}
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
}
Network object can have User, but I need to check first in database that given e-mail doesn't already exists or do some other checkings, so my commands will be executed successfully.
By adding user I mean adding completely new User to database.
Would it be correct way to do this?
You can do it the way you do it now and it's ok.
Another option is to make this Validation in Contoller. Then you should use Remote attribute. And Move your IsEmailUnique(string mail) method to Controller.
If you want to know how you can do it with email check - this question will help you.

C# WCF - Using entity from Service in client app

I am new in WCF and have simple issue. In my service library i have class :
[DataContract]
public class Player
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public int WinNum { get; set; }
[DataMember]
public int LoseNum { get; set; }
[DataMember]
public int PlayedTime { get; set; }
.
.
.
[OperationContract]
public override string ToString()
{
return UserName;
}
Service class look like this:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class PiskvorkyService :IPiskvorkyService
{
[DataMember]
public List<Player> Players { set; get; }
public PiskvorkyService()
{
Players = new List<Player>();
}
.
.
.
public List<Player> GetPlayers()
{
return Players;
}
From client app (WPF) i can add some new Players into list on server, it work well. But now i need to get Player list from service in client app and list them wherever I need. I tried something like this in client app:
var Players = ps.GetPlayers(); //ps is instance of PiskvorkyServiceClient();
foreach (var player in Players)
{
MessageBox.Show(player.ToString());
}
But for each Player in list i just get:
Could someone help me please?
Thanks
You have overridden the ToString-method on the service side
[DataContract]
public class Player
{
.
.
.
[OperationContract]
public override string ToString()
{
return UserName;
}
But methods are not serialized and therefor not transfered to the client side. This means you receive a player object with only its data (all fields and/or properties decorated with [DataMember]).
This is the default behaviour.
So your best choice would be, as #Chris already mentioned, to call player.UserName on the client side.
Some other approach you can follow is to extend your model via extension methods on the client side. So you can do something like the following:
public static class PlayerExtensions
{
public static string Print(this Player player)
{
return player.Username;
}
}
Then you can use your player-objects like this on the client side:
var player = new Player { UserName = "Nick" };
string plyerName = player.Print();
This has nothing to do with WCF, it simply that you are using ToString on an object which haven't overridden it, which means you'll get the default implementation, which gives you the class name.
Do either:
public class Player
{
...
public override string ToString()
{
return this.UserName; // or whatever you wish to print
}
...
}
Or:
MessageBox.Show( player.UserName );
Call player.UserNameĀ on the client side. var Players = ps.GetPlayers(); //ps is instance of PiskvorkyServiceClient(); foreach (var player in Players) { MessageBox.Show( player.UserName ; }

Categories

Resources