Title: Yegolev Learns C# - was Your app's data Post by: Yegolev on November 14, 2008, 05:18:15 AM As some of you know, I'm puttering with C#. Currently I have a simple idea for an app that I can use to teach myself several things but I need some help with direction.
My app is a sort of recipe book, which I creatively titled RecipeBook. Currently I have the easy bit working since I basically just used one of the examples from Head First C# to make a front end to a DB (MS SQL Server 2005) that contains recipe details. Now I have hit a wall where I know what I want to do but not how to do it: I can flip through the book and presumably search for fields, but what I would like to do is specify an ingredient (row) and have it show me all of the recipes (columns) that use that. At this point it should be obvious that I don't know the first thing about MSSQL, which is what I used here. I was initially going to ask about a good book for raw beginners.... My problem-solving training kicked in and lead me to ask the question: how is data storage and retrieval normally done in game development? I'm pretty sure it's not with a DB file. Once I get the answer to that, I can then ask other questions. Title: Re: Your app's data Post by: Trippy on November 14, 2008, 05:33:50 AM Need to see your DB schema.
However off the top of my head, for just the recipe part and ignoring a quantity column I would do something like this: Table recipe id: recipe id name: recipe name Table ingredients id: ingredient name: ingredient name Table recipe_ingredients id: whatever (or leave it out) recipe_id: id from recipe table ingredient_id: id from ingredient table Then given an ingredient by name you would do select recipe.name from recipe_ingredients, ingredients, recipe where ingredients.name = "<search name>" and ingredients.id = recipe_ingredients.ingredient_id and recipe.id = recipe_ingredients.recipe_id Title: Re: Your app's data Post by: Yoru on November 14, 2008, 06:17:32 AM Implicit joins make baby Jesus cry.
Code: SELECT recipe.name Title: Re: Your app's data Post by: Trippy on November 14, 2008, 06:38:18 AM Implicit joins make baby Jesus cry. The JOIN keyword was added long after I learned SQL -- i.e. that's the way people were writing SQL long before you were born! :awesome_for_real:Title: Re: Your app's data Post by: Yoru on November 14, 2008, 07:15:56 AM Ignorance of proper best current practices is no excuse. :grin:
Title: Re: Your app's data Post by: Yegolev on November 14, 2008, 07:50:17 AM You guys seem to feel it's OK for me to write a game with MS SQL, so next question: what is a good book for learning what the fuck you guys just wrote. :awesome_for_real: I have done a few queries on the Oracle DBs at work but that's not nearly the same as setting up my own data layout and figuring out how to query it.
Title: Re: Your app's data Post by: Yoru on November 14, 2008, 08:22:48 AM ... fuck. I just deleted a huge post. Fuck you, funny mouse buttons.
I learned SQL back in the 90s by hacking out database-driven websites, using tutorials on the web. If you want to learn more about it, you can almost certainly Google up a few beginner guides to SQL - the basics don't change much between the various flavors of the language. Once you have a handle on how to store your data, get it out and arrange it in a manner useful to you, you can move on to architectural topics like database normalization. That said, for game development, I think the answer is "it depends". MMORPGs almost universally use relational database systems - I'm aware of MMOs that use MSSQL, Oracle, and Postgresql. DAOC and some of the older games, if I remember right, faked a database and used structured files on disk/in memory instead; presumably, they had some proprietary layer that handled the ugly details of manipulating and querying that data. For non-MMOs, it's going to depend on your game architecture. If you're building it in an object-oriented manner (and you probably are, if you're using C#) all your runtime data is likely to be governed by objects in memory - precisely how would be a discussion of design patterns and proper object-oriented design. There's roughly a hundred thousand books on those topics published every few days. For your data on disk, well, I suppose that depends on what you're storing. Presumably you'd keep textures and models in an appropriate format that's exportable by your editing tools and readable by your engine. For static numerical or text data, you can probably just use CSV or tab-delimited text files. They're easy to edit in Excel and simple to parse. For crazy-ass binary data or complex data structures derived from your in-engine objects, you'll probably want to use an object serialization library. I found this (http://www.ondotnet.com/pub/a/dotnet/2002/08/26/serialization.html) when googling for "C# object serialization". The TLDR is: It depends on what data you're storing, what your game is, and what your game's engine architecture likes/can handle. Title: Re: Your app's data Post by: Salamok on November 14, 2008, 09:58:31 AM Need to see your DB schema. However off the top of my head, for just the recipe part and ignoring a quantity column I would do something like this: Table recipe id: recipe id name: recipe name Table ingredients id: ingredient name: ingredient name Table recipe_ingredients id: whatever (or leave it out) recipe_id: id from recipe table ingredient_id: id from ingredient table and this doesn't seem overnormalized to you for the task at hand? Title: Re: Your app's data Post by: Yegolev on November 14, 2008, 10:29:48 AM Further thought on this leads me to think it might be easier by far to implement my data structures as C# collections of an appropriate type, at least the static ones. Persistent data can be put into a CSV file, and this avoids me having to bother with how-to-read-a-dbf on machines without SQL Server 2005. I will have to think about it more in terms of the game I want to write.
If you extend the idea of the RecipeBook to a game that heavily implements crafting, you can see where it would be a great idea to generate a list of recipes that use a particular material. I would probably want to do this without using a .dbf file if I need to have a SQL service running to do it. Decisions, decisions. Will think on this more. Title: Re: Your app's data Post by: Yoru on November 14, 2008, 11:14:12 AM If your recipes are all pre-authored, you can generate a file that behaves like a cross-referencing table. It would be structured essentially identically to the relationship table Trippy posted above.
So, e.g., you would have 3 values per line, those being recipe identifier, material identifier, quantity. The naive way of querying this is to simply loop over every line, appending the recipe identifier to a list of recipe identifiers every time you came across a line with a matching material identifier. After that, it's simply a job of optimizing - for example, if you do this query a lot, you could construct a tree or hash table keyed off of the material identifier that gives you the list of recipe identifiers. You could do it up-front or construct it lazily as new material identifier searches are requested, and then caching the results. Title: Re: Your app's data Post by: Salamok on November 14, 2008, 11:59:13 AM Most windows boxes should have MSDE loaded and I believe you can include it in your install package. The entire point of MSDE is to provide a desktop based alternative to MS SQL Server, the theory being that you only need to change 1 or 2 lines of code to migrate your app from SQL Server to MSDE.
Basically sounds like ODBC to me but I stopped using M$ development products before MSDE came out, so i couldn't tell you how well that works. Having nicely normalized tables is all fine and dandy but if you over do it you will see a noticable negative performance impact when retrieving a data set that requires an excessive number of joins. For a prime example of this one only need to run a complex report or 2 in ACT and watch it grind away for several hours on a 10000 record database before spitting out it's report. Anyhoo, Structure aside if you store your data in a way that will allow you to use SQL for data handling you will be saving yourself a fair bit of code work. Title: Re: Your app's data Post by: Tarami on November 14, 2008, 06:21:03 PM I would avoid CSV - while it might be simple to imagine and plan, it's a complete bitch to maintain. You need very strong versioning because a single faulty value can screw up the entire data set.
XML queried with XPath (tutorial (http://www.w3schools.com/XPath/xpath_intro.asp)) could be a solution. It's quite verbose, but is instead very robust and easy to maintain even manually with a text editor (saves you having to write a tool early on). .NET has competent XPath support already in it, System.Xml.XPath.XPathDocument. It's pretty fast (it's memory-resident for the duration of the query), much faster than digging in a flat file would be. Another option is querying XML with XLinq (System.Xml.Linq.XDocument), which is powerful also for creating and shaping XML documents. Google-fu will teach you how. :-) As Yoru mentioned, binary serialization is yet another option. However, it'll get messy as it will not make a difference between an object reference which you want to serialize and one you don't (which can give you circular serialization, along with storing a lot of junk or directly corrupt data) unless you're being very conscious about adding the NonSerialized-attribute everywhere. There are simply no guarantees for what it will do to your objects when they get serialized and it will likely involve large amounts of debugging and trial&error. It's also very inconsistency prone due to this - if two objects reference the same object, they might serialize it differently because it has changed between the dumps. A huge thread-lock would solve that, but thread-locks are bad. Since .NET is a managed environment, there's no easy way to just dump memory to disk - because the memory is never under your control and what exists at a location in it at one line of code might be gone the next. Usually it's better and far more controlled to deal with binary I/O using System.IO.BinaryWriter/Reader, but that means alot of typing of (de)serialization methods in the data classes. As for memory-resident collections, I can only recommend System.Collections.Generic.Dictionary<TKey, TValue>, it's an implementation of System.Collections.HashTable and pretty much the fastest way in .NET to do random access in a collection. Also, easily overlooked, don't forget that the absolutely fastest, no contest, way of traversing data is simply by creating the references needed from objects to the data they need to find. Title: Re: Your app's data Post by: Yegolev on November 14, 2008, 06:32:20 PM Hey, I understood almost all of that! Except the last paragraph, which I have the odd feeling is something I really want to understand.
Tarami, since you haven't been playing Yegolev: The Home Game, I'll tell you up front that I have zero real programming experience. I can do Korn and Perl but that's it. I'm basically chewing my way through O'Reilly books to learn C#, which so far seems to sit between Perl and C in difficulty. So if you start talking about heaps and pointers and what not, I might not get it. I probably know what you mean by creating a reference to your data directly (in a generic dictionary, I guess?), but I would like a better explanation of the details in your last paragraph, please. Title: Re: Your app's data Post by: Tarami on November 14, 2008, 06:57:36 PM I'll try to explain, this is the most common way I deal with caching when I have multiple "tables" of correlating data.
Create a class for each table that can store one row of data (let's say Recipe and Ingredient). Recipe should have a list of all the IDs of Ingredients it needs. Create a Dictionary<,> for each class you just wrote. Pick TKey (the type of the key, such as string, or int, or anything class/struct you like) according to what you want to search by in the specific Dictionary. For example, for a list of recipes, you might want to search by ID, so that would be TKey = int. The TValue is the class for the data you're going to store. Fill all the dictionaries from your datastore, doing _dicRecipies.Add(recipeId, recipeObject) until they're all in. Now all your data is memory-resident. Go back to your recipe class. Since you know you'll need to look-up all the ingredients for each recipe, you can add a List<Ingredient> to that class. The same goes for your Ingredient class - you can add a List<Recipe> to that class, to hold all the recipes that uses that ingredient. By now, you got a possible cross-reference between Ingredient and Recipe (a one-to-many association (not a relation, that's different ;-), one recipe links to multiple ingredients, one ingredient to multiple recipes). Go back to the method where you added all the Recipes and Ingredients. The complex bit: Loop over the whole dictionary holding the Recipes (this can be done with foreach(var item in dicRecipes) { ... }, where item will be end-up being a KeyValuePair<,> ), then search the dictionary over Ingredients for each Ingredient-ID you got in the Recipe. Add that Ingredient directly to your List<Ingredient>, so you got a true memory reference to the ingredient, instead of just an ID. At the same time, in that Ingredient, add the Recipe you're currently adding Ingredients to, to that Ingredient's Recipe list. This will give you the result of two inter-linked, searchable dictionaries, where each entry knows all the references the other dictionary got to it. So getting all recipes for an ingredient would be as easy as: List<Recipe> relatedRecipes = dicIngredients[theIngredientId].RecipeList; or List<Ingredient> neededIngredients = dicRecipes[theRecipeId].IngredientList; It'll be one single search, regardless of which direction you want to page, once you got it all running. Title: Re: Your app's data Post by: Margalis on November 14, 2008, 09:00:50 PM My problem-solving training kicked in and lead me to ask the question: how is data storage and retrieval normally done in game development? Some sort of text/script variant or roll-your-own binary. Title: Re: Your app's data Post by: Viin on November 14, 2008, 10:22:48 PM I just wanted to point out that a lot of games don't use the relational aspects of DBs, because it's too slow. They typically use a format that's very fast at lookups and searching, and use cross reference tables that are generated when data changes (sorta like Views but only created once). That's often why devs can't add more data to a window, because their "view" only pulls in certain fields from different tables.. they would have to rebuild that view (potentially gigs of data) to have access to more fields.
Granted, this might be old (I've been out of game dev circles since about 2000), but even today most fast transaction oriented DB implementations I've seen do not use any relational data structures if they can avoid it. Title: Re: Your app's data Post by: Yegolev on November 15, 2008, 07:19:02 AM This is excellent stuff, particularly Tarami's description. Looks like the way to go is to use memory structures and if I want to persist something, I'll fabricate some simple text/binary dump. I now have some more direction, too, which is awesome. Tarami, I.O.U. One Beer.
1. Implement Tarami's idea, which looks like a totally awesome learning experience. I haven't done anything with dictionaries yet. 2. Work on writing out data to a file format of some sort. (thanks Margalis and Viin) 3. Look at how to add new ingredients and recipes via the GUI. Title: Re: Your app's data Post by: Tarami on November 15, 2008, 01:12:24 PM You're welcome. :-) Post or PM if you need help getting started with the writing/reading to/from disk. There's a dozen ways to do it, some better than others.
Oh, and breakpoints (for debugging) are your friends. Use them EVERYWHERE and step through the code with F10 (step over)/F11 (step into). Title: Re: Your app's data Post by: Tarami on November 26, 2008, 04:24:51 PM So, howza goin'? :drill:
Title: Re: Your app's data Post by: sidereal on December 04, 2008, 09:53:53 PM I'm a late-coming jerk, so ignore me if you're already halfway done.
I would strongly recommend using a free off-the-shelf persistence engine, because serializing data to disk on your own is fraught with problems and is an unnecessary reinvention of the wheel (unless you just want to learn how it works). Specifically, I'd recommend Berkeley DB (http://www.oracle.com/technology/products/berkeley-db/db/index.html), which will manage persistence, transactional integrity (if you're multithreading), optimization, and so on. Also, JetDB exists but I have little experience with that. Title: Re: Your app's data Post by: Tarami on December 05, 2008, 01:34:14 AM I was under the impression that Yegolev both wanted to avoid external runtimes and learn how it was done. There's a gazillion ways to write data to disk that's safer than BinaryWriter, but none that's as hands-on. (Well, except fopen, fwrite :-P)
Title: Re: Your app's data Post by: Yoru on December 05, 2008, 01:45:06 AM Real programmers write a program that controls a pair of robotic arms that hold bar magnets, and use THOSE to flip bits on a disk.
Title: Re: Your app's data Post by: Tarami on December 05, 2008, 01:59:22 AM Real programmers write a program that controls a pair of robotic arms that hold bar magnets, and use THOSE to flip bits on a disk. But we aren't real prologammers, so we cheat.Title: Re: Your app's data Post by: sidereal on December 05, 2008, 09:02:54 AM I was under the impression that Yegolev both wanted to avoid external runtimes and learn how it was done. There's a gazillion ways to write data to disk that's safer than BinaryWriter, but none that's as hands-on. (Well, except fopen, fwrite :-P) If by runtime you mean a separate process, BerkeleyDB runs in your app process space. It's just a library that handles all of the transaction and query goo for you. As is Jet. But yes, the best way to learn is to do. If learning about the difference between row-level and table-level locking is what you're interested in. Title: Re: Your app's data Post by: Tarami on December 05, 2008, 10:42:05 AM By "runtimes" I primarily mean libraries, but I guess it can encompass other kinds of extensions aswell. MS Jet is "okay", never used Berkeley so I can't say which one is a better DB host. I'm guessing Berkeley. That doesn't seem to have a .NET wrapper though, so in this case it might be disqualified.
Taking it a little bit out of context here, because I'm not sure what Yegolev is really looking for: Don't get me wrong, relational DBs are great, transactional engines are great, but at the same time as they offer a ton of functionality, they also introduce a ton of cryptic behaviour that is specific to that engine. In the best of cases, a relational DB is just awesome because of the data transformation power it means, but I wouldn't say learning to use a DB is cake. It takes most people years just to get a decent grasp of one dialect of SQL and even when they do know it fairly well, it's nearly worthless in another engine. If you don't know DB basics to begin with (but have a decent understanding of bits and bytes) and just want to store some data, I wouldn't want to impose the huge luggage of learning a DB engine. To a professional it's a no-brainer, but DBs also come with alot of weird restrictions (that adept users view as features) for reasons that are everything but clear. Mangling of some in-memory structures is much more straight-foward, I dare say. I think for these reasons, XML is really the first thing I try to teach someone new to the whole data storage deal. It's structured, supports automatic (de)serialization well and it's just text that you can modify in an editor. Transactions and locks are usually requirements that come quite a way down the line. Beginners need to be pragmatic, or they'll drown in things they "need" to learn before they can even start doing what they wanted to do to begin with. Most people want to see some results, not necessarily do it "the right way". Well, that's just my view on it. Title: Re: Your app's data Post by: Yegolev on December 08, 2008, 12:05:50 PM How's it going? Slowly. I had to switch gears back to Korn and rewrite a script that needed attention. It now actually works in 99.999/100 cases (estimated :oh_i_see:) rather than silent failures in certain places. After that I ran into a situation in another Korn script which set off the Perl light, so I again shifted gears and spent some time recreating it in Perl. Furthermore I had to remind myself of a bunch of Perl things, this time adventuring in filehandle input filters and the vagaries of system(). Now I'm hoping my mind shifts itself back to C# so I can actually implement a data structure as previously discussed.
What I am trying to achieve can be described in Tiers, I suppose. Make A Game And Let People Play It (I'd rather not put MSSQL into the distro) Make A Utility That I Use (same as above) Write A Nontrivial And Original C# Program Learn How To Program C# There are multiple motivations at work here and I neither desire nor need a robust data persistence scheme other than regular files... not unless I decide I want to go multiuser but I'm not pretending I will be ready for that anytime soon. I was thinking using a DB would be awesome, but I am quickly seeing that it is overkill for any of my goals, and perhaps even counter to the lowest Tier. I'm definitely not afraid of making system calls, or calling methods in a non-managed DLL, I just don't know how yet. :awesome_for_real: Anyway, once I implement some sort of structure like Tarami outlined, I'll come back and share progress. Title: Re: Your app's data Post by: Tarami on December 08, 2008, 01:47:59 PM Don't worry about making going outside managed space, it's virtually impossible to do by mistake in .NET. The garbage collector and disposal routines will clean up pretty much any slop you leave, including open network streams and lingering file handles. It used to be sorta sensitive to that kind of abuse, but since 2.0 it's really robust. 99% of what you need either exists in the framework already or you can download a ready-made wrapper.
Although, if you really really want to, visit http://www.pinvoke.net/ for your reckless API fix. :drill: Title: Re: Your app's data Post by: Murgos on December 08, 2008, 02:10:32 PM Anyway, once I implement some sort of structure like Tarami outlined, I'll come back and share progress..."I had to remind myself of a bunch of Perl things..." If you are familiar with Perl then the structure that Tarami outlined should already be fairly familiar. The Dictionary Object is a hash table, same as Perl hashes, that's been extended with a few features. The custom classes he is referring to are just something to hold an Array or List. Don't let the lexicon daunt you. Hashes of array's (or hashes of hashes or arrays of hashes, etc...) are pretty common in Perl, and almost every other language, so if you haven't seen this sort of structure before this is something you will want to pay attention to and put in the extra effort for so as to be sure you get 'it'. As an added tie in with what some of the above posters said, and for even more practice: once you have generated your hashes of objects with lists there is no reason to have to do that every time you run your program. Use serialization to write them out to a file on exit and read them in (if they exist) on load. If you have several thousand recipes, like my GF does, then it could save a good bit of time over reading from the DB and looping around to populate your hashes each time you run the program. :rock: Title: Re: Your app's data Post by: Yegolev on December 09, 2008, 06:06:49 AM If you are familiar with Perl then the structure that Tarami outlined should already be fairly familiar. Actually, that's the biggest reason I got what he said. I recognized the structure as cross-referenced hashes. Probably my single biggest milestone in Perl was when I wrote a loop to slurp /etc/qconfig into an anon hash of hashes so I could pick out data at random, because that fucking rocked. If I can translate that knowlege into C# then I'm halfway there. Not in a Bon Jovi way. As an added tie in with what some of the above posters said, and for even more practice: once you have generated your hashes of objects with lists there is no reason to have to do that every time you run your program. Use serialization to write them out to a file on exit and read them in (if they exist) on load. If you have several thousand recipes, like my GF does, then it could save a good bit of time over reading from the DB and looping around to populate your hashes each time you run the program. :rock: This is a stellar idea and confirms my idea of avoiding a DB. I feel ready! Title: Re: Your app's data Post by: Tarami on December 11, 2008, 04:10:42 AM Unfortunately it will not work. Serialization doesn't refresh memory references (for good reason).
Some code (image for easier reading): (http://img241.imageshack.us/img241/67/kewdem1.gif) C&P code here: Sorry for SirBruce'ing it up like that. Point is, even if you serialize the dictionaries, you will have to manually rebuild both dictionaries, because they'll no longer be linked via shared references. Every item in each of them will have its own instance, which means you can't make a change that will affect both. Ergo, if you change something and re-serialize them, they will differ because the change won't "propagate" to the other dictionary. Title: Re: Your app's data Post by: Yoru on December 11, 2008, 08:31:31 AM Dude, what IDE has a "copy for messageboard" command? :drill:
Title: Re: Your app's data Post by: Tarami on December 11, 2008, 08:44:29 AM Dude, what IDE has a "copy for messageboard" command? :drill: It's a GIF. :wink: Other than that, it's just Visual Studio, themed for undead. :vv: Title: Re: Your app's data Post by: Murgos on December 11, 2008, 09:11:38 AM Ok, so it might get a little more complicated. Serialize out the data and not the dictionaries and rebuild the references when you read it back in. It will still be a fun experiment.
Though, getting some RDB experience, enough so that "Select * from myTable where blah == blech" isn't something that gives you pause is pretty worth while too. Title: Re: Your app's data Post by: sidereal on December 11, 2008, 10:53:53 AM Really, BerkeleyDB is perfectly setup for persistence like this.
There are lots of ways of getting around the problem of deserializing constants. One is to maintain a static pool of keys that deserialization pulls from. (With Strings and Java, this is easily done with String.intern(). I'm sure there's a C# equivalent). Another is to just avoid dependence on key reference equality. If your keys are any primitives or Strings, this is fine and probably good practice anyway. Title: Re: Your app's data Post by: Tarami on December 11, 2008, 11:32:47 AM I used a string because it's a reference type and it served its purpose in an example. Typically you're not storing strings but your own types, which are not interned by the framework and thus you don't have an object repository unless you whip one up yourself. Which returns to the point I was arguing from the very beginning; serializing two key-unique lists and then building reference lists on load (or demand) is in almost every regard a better solution. Rolling your own serialization around something as structurally simple as a hashtable is quite a bit of work to no real end.
Title: Re: Your app's data, or Yegolev learns C# Post by: Yegolev on January 02, 2009, 11:16:32 AM Alright, time has passed and I made an effort to implement something. I may or may not have understood Tarami's directions, so I decided to KISS and see if I can just get the data structure part working for now by putting raw strings in. I made some classes:
class IngredientNames { List<string> myIngr = new List<string>(); myIngr.Add("Onion"); myIngr.Add("Water"); myIngr.Add("Squash"); myIngr.Add("Leaves"); myIngr.Add("Nuts"); myIngr.Add("Cheese"); } class RecipeNames { List<string> myRecipes = new List<string>(); myRecipes.Add("Soup"); myRecipes.Add("Casserole"); myRecipes.Add("Pasta"); myRecipes.Add("Salad"); public RecipeNames () { Dictionary<string,string> recDic = new Dictionary<string,string>; foreach (string s in myRecipes) { recDic.Add(s,IngredientNames) // underpants gnomes? } } } I'm reading Programming C# 3.0 as hard as I can but the whole OO business is still not quite grokked and so I'm unsure about the best way to get the values for myIngr into recDic. I want to do something like a double-foreach like how I would poplulate a 2D array, but I am also not sure how to get that to work. At this point I don't care if the data are paired up in any particular way, I just want to get the fill-in-recDic part working and I'll fiddle with that later. What my insitncts tell me is that I should have the recDic bit in some third class but once again am unsure of what the best way would be to get the list from those other classes. Title: Re: Yegolev Learns C# - was Your app's data Post by: ezrast on January 02, 2009, 12:41:03 PM You want every recipe name to be the key for a number of ingredients, right? Dictionaries are one-to-one, so recDic should probably be defined as a Dictionary<string, List<string> > or something.
recDic.Add(s,IngredientNames) is probably failing because Add is looking for strings as arguments, and IngredientNames is not a string. But at this point I should probably keep my mouth shut and let someone who knows what they're talking about answer. I do not know C#. Anyway, more importantly, remember to post the compiler error next time. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 02, 2009, 04:21:17 PM You are trying to add a string collection to a dictionary that is typed to hold single strings, which will not work... also, the myIngr list is private, meaning it can't be accessed outside of the class, which will halt the compiler even if it's fixed.
But you got it sorta right, you just need to think of objects more as... well, ordinary objects. :-) If you rearrange it a little, it'll be easier to see, I think. OO is primarily a way to impose context on code, telling code implicitly on what data to run the logic, rather than having to pass a reference around for the thing you want modified. Therefore it helps if you simply think of objects in OO as any object you got in real life. If it can have a name, you should add a variable to the object that associates a name to the object. In this scenario, you got ingredients and recipes, both of those making for very straight-forward classes; Code: class Ingredient { } Each of these can have a name, such as "Flour" or "Sweetrolls", so add a string variable to hold that info. Code: class Ingredient { Recipes are made up by several Ingredients, which means we have to keep a list of the ingredients each recipe needs. Since each recipe has its own distinct list, we can add that info directly to the Recipe class, because no other recipe needs to access that information. It is only relevant to the recipe itself. Code: class Ingredient { Now you can handle Ingredients and Recipes for what they are; self-contained objects that have all the info they need to act independently of eachother. We will do one more thing to each of the classes to ease the usage of them - add a constructor that takes a name parameter. Code: class Ingredient { Then we can initialize a recipe; Code: Recipe casserole = new Recipe("Italian Casserole"); That will give you an object, with the Name "Italian Casserole" that has three Ingredients. Now you can add the Recipe to a "cookbook" of sorts, namely a dictionary. A cookbook is a collection of recipes, and each recipe has a distinct name. That makes a Dictionary<,> perfect. Code: Dictionary cookbook = new Dictionary<string, Recipe>(); // String, for the name of recipe, and Recipe, that holds all the relevant info. Now you can go on and make more recipes the same way and add them to your cookbook-dictionary. To pull out a certain recipe, you only need to do; Code: Recipe momsCasserole = cookbook["Italian Casserole"]; Of course, now you got the problem that you only have the ingredients, but lack the quantities. That's a little more tricky to do in a proper way, so I'll leave that out for now. I hope it helped anyway. :grin: PS. I leave no guarantees that the above actually compiles, but it should do with minimal fixing. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 05, 2009, 11:02:01 AM Please indulge some SirBrucing so we can see if I get what you are saying.
You are trying to add a string collection to a dictionary that is typed to hold single strings, which will not work... I sort of thought I could use references for values but that is very Perly and perhaps incorrect anyway. Quote also, the myIngr list is private, meaning it can't be accessed outside of the class, which will halt the compiler even if it's fixed. Later, in the car on the way home, I had the idea that I was being sloppy and should have written accessors, properties or something, for the class that just has a list, but then I wondered if I would gain anything by putting the list in a separate class. Quote But you got it sorta right, you just need to think of objects more as... well, ordinary objects. :-) If you rearrange it a little, it'll be easier to see, I think. This statement seems to sum up my primary problem with C#. In Perl I can just write everything with a global context, and it defaults to such behavior, but this just isn't how things are done in C#, or any Real OO it seems. I was going to try to put your linked-dictionary idea into code but it seems like I'd be better served by laying out my object scopes as if I was going to write the actual program, then get into the rest of it. Quote Code: class Ingredient { } Each of these can have a name, such as "Flour" or "Sweetrolls", so add a string variable to hold that info. Code: class Ingredient { Recipes are made up by several Ingredients, which means we have to keep a list of the ingredients each recipe needs. Since each recipe has its own distinct list, we can add that info directly to the Recipe class, because no other recipe needs to access that information. It is only relevant to the recipe itself. This stuff is always incredibly obvious in hindsight. "Recipes are made of Ingredients" indeed. Quote Code: class Recipe { Oh yeah. I get it. Makes so much sense now, list of objects in the Recipe class. Well, it makes sense now, but we shall see what happens next time I try to do this without handholding. The thing that seems to trip me up is illustrated here: I'm just not used to the idea of making a class just to hold a string or even a set of strings, which is what I think an Ingredient would be since it doesn't have many properties other than the name. Really the idea of turning something dead-simple as a string into a class seems ridiculous, and I am just going to have to overcome that perspective, I think. Quote Now you can handle Ingredients and Recipes for what they are; self-contained objects that have all the info they need to act independently of each other. I'll need to keep this general idea in mind or else I'll be in trouble again. Quote Code: class Ingredient { I'm positive I get this. I know how to make a constructor. I am trying very hard not to think of this sort of thing as "ridiculous". I have to remind myself it's probably like how I thought calculus was ridiculous until about halfway through differential equations. :awesome_for_real: Quote Then we can initialize a recipe; Code: Recipe casserole = new Recipe("Italian Casserole"); I am unclear as to where this code should go. Can I newb it and stick this in Main() in Program.cs? Or worse yet, Form1.cs? Quote That will give you an object, with the Name "Italian Casserole" that has three Ingredients. Now you can add the Recipe to a "cookbook" of sorts, namely a dictionary. A cookbook is a collection of recipes, and each recipe has a distinct name. That makes a Dictionary<,> perfect. Makes sense so far. It's a hash except using classes instead of references for the value... which is basically the same thing. Quote Code: Dictionary cookbook = new Dictionary<string, Recipe>(); // String, for the name of recipe, and Recipe, that holds all the relevant info. You lost me, maybe. This makes my head hurt. Let's see, you're instantiating a Dictionary object called cookbook. This cookbook contains pairs of string,Recipe. Then you Add a casserole with casserole.Name as the key... but what the hell is the string under there? Is it "Italian Casserole"? Well, it would have to be, so why does this seem totally ass-backward to me? :uhrr: Quote Of course, now you got the problem that you only have the ingredients, but lack the quantities. That's a little more tricky to do in a proper way, so I'll leave that out for now. I'll take a whack at that. If I get this example working then Encapsulation means I can't fuck it up too bad just by fiddling with Ingredient, yeah? :awesome_for_real: Quote I hope it helped anyway. :grin: It did, very much. I'll be back. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 05, 2009, 02:01:15 PM Properties are nice and should be used, but I didn't want to introduce them here since it would also introduce access modifiers (private, internal, public, protected etc.). Thing is, properties don't really serve much of a point from a logic standpoint, but it's nice to have since you can write-protect variables with them and wrap some bits to tidy up the vars before they get stored internally. If you already know how properties work, go for it. I chose public variables because it was a little cleaner and hopefully easier to follow.
Yes, the add operations should be stuck in a main() or Form_Load() or some such start-up method. It doesn't really matter, but you will have to keep in mind to move the declaration of the Dictionary outside the method (into the class Program or Form1, depending on what kind of application you're making) so it won't go out of scope between calls of utilarian methods (perhaps an Add event when you click a button, or some such.) Something like; Code: class Program { Not sure I'm getting this right, but - The underlying value of casserole.Name is "Italian Casserole" as you say, it's just because of how Dictionary<,> behaves that you have to "double declare" the key. In theory you could be keying your stored objects by some other string that didn't exist in the object that you're storing as a value. But that would be stupid in this case. ;) It might seem more elegant to simply tell the dictionary to use the Name variable, and it would be, but that would require a little bit of unnecessary tampering, which Dictionary<,> is there to avoid. PS; Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 05, 2009, 02:59:14 PM Really the idea of turning something dead-simple as a string into a class seems ridiculous, and I am just going to have to overcome that perspective, I think. It does seem now like excessive busywork, but one of the major benefits of OO development is separating architecture from implementation. In non-OO perl-land, my implementation defines my architecture. So if I store my ingredients as a list of strings, I interact with it in one way (looping the list), but if I store it as XML I interact with it another way (sax parsing), or if I store it as a bunch of constants with 'amount' fields I have to do some multidimensional hash and all of the code that uses it has to be tuned to that. With OO you abstract that implementation away, so as far as the rest of your architecture is concerned you have an Ingredient and could give fuckall about how it's stored. So if you want to change from a String to a reference to some global 'Nutmeg' object (and I assume that you will eventually want to do something like this), you don't have to dig through all of your code to change it. You just change the internals of the Ingredient class, which for now is, yes, just a String. It makes the extra 5-10 lines worth it. Or to put it another way, right now it's 'something dead-simple as a string' but you don't want to bet 30 hours of future development time that it's going to stay that way. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 05, 2009, 04:07:41 PM I want to chime in on the abstraction point there. Yes, Ingredient is important because it's conceptually very different from having it as a string. I just like metaphors too much, so I'll just use a couple bad ones to explain the point.
The thing is, a string is words. That's all it's good for storing. An ingredient is not words. You don't put words in food. You put things like carrots in food. Carrots have weight and colour and all sorts of qualifying attributes. Grated carrot or diced carrot? Meanwhile, the string "carrot" doesn't tell you any of that. But the class Carrot, (which may inherit Ingredient, perhaps ;-), can tell you as much as you like about its properties. It *can* still be just a carrot, because it's that too, but it can also be a quarter-pound of grated carrot that was grown in Montreal. Maybe you took it so far that you made a subclass of Ingredient, Vegetable, and you have a recipe for vegetable soup that just asks for two pounds of random vegetables. Well, you have Vegetable defined, so you know exactly what a Vegetable is. Still, you have no idea if the string "carrot" is a vegetable. It lacks meaning to your logic beyond being a six-letter word. You could expand the string to "Carrot, grated, 0.2 pounds, grown in Montreal, is a vegetable, is an ingredient", but that just doesn't seem very practical in comparison. However, there's no ultimate truth to what should be abstracted and how far it should be abstracted, that's just up to common sense and probably one of the big things to learn in OOP. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 07, 2009, 12:43:52 PM My mom always said I didn't have any common sense. sidereal's "architecture vs implementation" bit is my trouble, being a self-taught hacker instead of a real programmer. It's going to take some hammering to get my mind into the right frame for this style of code, but I'll get there, and you guys are helping a great deal. I have already run into the dreaded "what should be a class and what should a class be" question when trying to dream up something. For my card game, I still don't know if a Hand should be a class that contains Card instances, should there be a Deck class or what. Mind-numbing.
I know about properties (another thing which also seems sort of silly at first glance) because they are tossed in there with everything else when Jesse Liberty explains variables and why you should not make everything a global. Ironically, I think I'd rather do something fancier here. Quantity was mentioned, but there are two possible quantites: quantity to use in a recipe and quantity in stock. A Recipe that calls for two cups of sugar should, I think, have "two cups" in the particular Recipe, so that should not go into Ingredient, but Ingredient should know how many eggs are available... well, later on. Good thing Ingredient isn't just a string! :awesome_for_real: Title: Re: Yegolev Learns C# - was Your app's data Post by: MahrinSkel on January 07, 2009, 02:01:46 PM Being self-taught myself, I understand your frustration. I found it works best if I just hack together something that barely works (but is ugly as sin, lots of brute force and spaghetti code), then use the understanding of the problem that gave me to plan a proper object hierarchy and do it right, usually from scratch (bits of the old code may get cut and pasted in, but I start from a new project).
--Dave Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 07, 2009, 02:21:49 PM Yeppers. Coding is like most crafts; getting the bits to work together is initially much more important than doing it strictly right. I believe it goes back to the whole abstraction dilemma; it takes a very, very experienced programmer to know what systems you're going to need to make stuff work. Most likely you know because you've built something similar before. Therefore it's almost always more efficient to churn out some choppy, ugly prototype that works as a concrete blueprint for building all the clever bells and whistles the final product will need to work smoothly. Also, it's good for the soul to see it can work before you put in a ton of hours to make it fancy. Going full force first thing with something you're not familiar with, only to see it's much more complicated than you expected, is horribly demoralizing. Many are the projects that I've started that have ended up in the zombie pile.
Secondly, actually I'm going to sorta agree with you on properties. They are rather silly. The actual need of properties is none. They just reduce code clutter and the tidy up the programming interface for the class. Which is nice, of course. :-) And having a Hand object, if you want one, made up by a collection of Card objects is a good idea, because that's what it really is. A (random) set of predefined cards. You can then add hand manipulation methods to the Hand class, since they only need to tamper with the cards actually on hand. And so on. Maybe a method to draw a number of cards in the Deck class? Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 07, 2009, 03:31:56 PM For my card game, I still don't know if a Hand should be a class that contains Card instances, should there be a Deck class or what. Mind-numbing. To pile on the sidetrack, when I do card games I usually share a class between a deck and a hand. I just call it a CardPile, which is a thin wrapper around a native collection (a List or whatever). I try to distinguish between what something is (a pile of cards) and what it's used for. Decks and hands are used for different things, but they are essentially the same thing, so they can share the same functionality. OO purists would disagree with me, because some of my functions are useless depending on what this pile is used for (for example, I support shuffling your hand which doesn't amount to much [or does it?]) but I think the simplicity of the architecture makes up for excessive functionality. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 12, 2009, 11:30:17 AM It's not much of a sidetrack since my initial drive into C# was due to wanting to write a better MTGO than WoTC. I'm still pretty sure I can do it. A RecipeBook is possibly more complicated than a card game so maybe I should revisit the original idea.
I had the same problem with a Hand made of Cards. I mean, that's obvious to me, but then I was looking at making a Deck object. The problem was oddly similar to the one I worried about with RecipeBook: keeping track of which Card instances were where. Did I need some other object, or should I use global vars (these seem anathema to C#)? Abstracting it to PileofCards does seem like a great way to simplify it while I pound out a prototype. Hell, if I can instantiate a few cards and "move" them between Piles, that will be a major breakthrough. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 12, 2009, 12:04:58 PM Do that. Also, the Graveyard is a CardPile. The OutOfPlay area is a CardPile. The InPlay area is a CardPile, etc. Or you could just call it a CardCollection since they're starting to look less like piles. But if your CardWhatever class supports shuffling, drawing from one to the other off the top, moving a card from one to the other, then you're 80% of the way there.
Quote write a better MTGO than WoTC Ouch. I appreciate the sentiment, but ouch. There are two ways to go about this. One is to implement a blank card table that supports moving cards around and displaying them. It is up to the players to understand the rules of Magic. So, for example, a player decides to play a card they manually dock their own mana, click the card to play it, click on the table, etc. The other player keeps them honest. In this case the software is just replacing the physical cards, the shuffling algorithm, and the table display. This can also be just a step towards option 2, which is.. actually implement the rules of magic. This is not for the faint of heart. I made a run myself back in 98ish. Just getting through basic creatures, direct damage, and other non-mindbending cards is tricky. Once you get into the real juice like Time Vault that breaks the game's own rules, you are in super-expert data modeling land. You need to be very careful about how you implement the engine to make sure a card can go in there and completely fuck it up and then back out when the card goes away. Just read the Lich (http://www.moxdiamond.com/Magic-The-Gathering/Card//Lich.html) effect and try to come up with an architecture that supports that. To be fair, if you limit yourself to tournament-legal cards you get rid of most of the ridiculously complicated, game-breaking stuff. But it's still a real challenge. Title: Re: Yegolev Learns C# - was Your app's data Post by: schild on January 12, 2009, 12:58:53 PM The problem with MTGO isn't "the cards don't work." This is, in fact, the one thing it does right. No, the problem is the "constructed" type of play - not in the Magic sense - but in games with rules like "Drafts" and "leagues." Also, client stability and the GUI.
God. The fucking GUI. Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on January 12, 2009, 08:47:00 PM Write code that makes logical sense. That's my number one tip. 95% of tough coding problems are caused when someone writes a bunch of code and they don't understand how it works or how it is *supposed* to work, they just throw more and more stuff together until it collapses.
If you have a clear idea of what peice is supposed to do then smushing them together is trivial. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 13, 2009, 10:57:37 AM actually implement the rules of magic. I don't want to do this, actually. I started playing tournament Magic in 1994-5 and I am very impressed that anyone created a successful computer version, online or not. No, my intent isn't to duplicate the cards or rules but rather to create a similar game that does not look like the programming team was intentionally trying to sabotage their own project. I have my own idea for a card game, anyway. Steps on the card game are pretty much as you outlined. Set up a system where I can move Cards from one CardCollection to another, then set up a simple display using simple Form elements or maybe WPF, then whip up a GUI in three hours. Yes, I am kidding about the three-hour GUI. Title: Re: Yegolev Learns C# - was Your app's data Post by: Morat20 on January 13, 2009, 12:21:04 PM I want to chime in on the abstraction point there. Damn skippy. For the longest time I avoided real abstraction, having been raised on C where you had structures and you LIKED them, goddammit!After wasting countless hours rewriting code simply because I hadn't bothered to do it properly (abstractly!) in the first place, I stopped fucking around. Yeah, it takes forever to write a properly abstracted "hello world" program. It's not for that. It's so I can have MyObject.Clone(AnotherObject) and written right there in main or on_load or whatever, and know that no matter what I do to how MyObject works, that bit will ALWAYS work. (Assuming I changed the Clone method, of course). Abstraction saves you days or weeks of work down the line, at fairly minimal cost up front. I created a genetic program -- top level ran it (Initialize, rank for fitness, crossover, mutate, repeat), middle level was the tree-structures of the chromosome, next level down was the "Rule" level (nodes and leaves) and below that was the final layer of actual data. I rewrote the entire bottom layer -- completely changed the data it was running on, how it compared them how it stored them, EVERYTHING -- without having to do more than cosmetic changes anywhere else. Even though that bottom layer was where everything actually happened, the absolute core of the project. All because I'd written it properly in the first place. If I'd done it in a language without OO support, I'd have had to pretty much rewrite half of it. OO, done properly, allows for extensibility, adaptation, evolution, and all the other bits that happen naturally with any good piece of code. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 13, 2009, 04:41:54 PM I want to chime in on the abstraction point there. Damn skippy. For the longest time I avoided real abstraction, having been raised on C where you had structures and you LIKED them, goddammit!Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 14, 2009, 05:46:31 AM For the longest time I avoided real abstraction, having been raised on C where you had structures and you LIKED them, goddammit! After wasting countless hours rewriting code simply because I hadn't bothered to do it properly (abstractly!) in the first place, I stopped fucking around. Yeah, it takes forever to write a properly abstracted "hello world" program. It's not for that. It is very strange that you post this on the same day that I run into a real-world issue with this very thing. I was scratching my head (in between constant interruptions) over why I was not seeing some notifications come out of a Korn script I have been working on for a few months, and while I was waiting for my haircut it occurred to me that it was probably due to restrictions on recursive POSIX function calls in Korn. Basically you can't recurse POSIX functions so you have to use Korn functions for that purpose... trouble is that the scoping is different. I inherited the script, so it's not 100% my fault in this case, but the reason I stuck with POSIX functions is because I was lazy: variable scoping is global by default. Switching to Korn functions means that I have to rewrite a bunch of shit. Goddammit. If I had done it the "right" way the first time then I would not be in this situation... although when you are talking about ksh the definition of "right" is very loose. The above is why I really want to learn C# the right way. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 14, 2009, 06:43:14 AM it occurred to me that it was probably due to restrictions on recursive POSIX function calls in Korn. (http://img.photobucket.com/albums/v109/themorningstarr/barry.jpg) "Yeah, I was WAY off about that." Title: Re: Yegolev Learns C# - was Your app's data Post by: Morat20 on January 14, 2009, 08:44:22 AM I want to chime in on the abstraction point there. Damn skippy. For the longest time I avoided real abstraction, having been raised on C where you had structures and you LIKED them, goddammit!Instead you used custom data structures and a lot of referencing and function pointers to do many of the same things as OO code does, but it was five times more complex, buggy as shit, and easy to break. But I learned that way, so I stuck with it (my C++ code was a lot more "C" than "C++") until I got tired of shooting myself in the foot. I don't bother with objects or the like if I'm doing something simple, but anything complex -- especially if I plan to revise/evolve down the line -- I start with an OO framework. I'll be a happy man if I never have to see -- or fix -- someone's bastard version of polymorphism using function pointers. Fucking function pointers. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 15, 2009, 11:43:27 AM Again I will agree with Morat with my example. I made a few small changes to my script, since as far as I could tell the code was fine and the meat was wrong, however I had to search variable names in the whole script and had to check each function that I impacted with my changes. Basically, I had to make sure that my various code bits were correctly handling the data they were working on since I could not guarantee any of the inputs or outputs.
I blame BASIC. Title: Re: Yegolev Learns C# - was Your app's data Post by: CadetUmfer on January 16, 2009, 06:38:42 AM I'm waiting for next year's thread where you learn F#, hehe. "What, you mean I can write this with 20% of the code and have it more abstract, more strongly typed and more succinct? Explain this closure thing again??"
;) Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 16, 2009, 06:50:26 AM Is that MS Fortran? Jesus help me. :oh_i_see:
Edit to say that if I decide to pick up any other language that has tenuous relations to my work, it will likely be Haskell. Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 16, 2009, 07:08:43 AM One useful thinking pattern for OO (it may or may not be useful for you) is to think about is-a and has-a. Things that fall under is-a should be defined as classes, while things that fall under has-a should be defined as a property/field of a class. An Ingredient has-a name, has-a freezing point, has-a burning point, etc.
One other thing I've noticed you do Yeg, is use "object" and "class" sorta interchangeably. This can lead to all sorts of confusion. To simplify (to the point of being not 100% accurate, but useful for a new/medium developer), think of a class as a data and behavior definition. Classes are templates of a sort. Objects are concrete instances of the classes you instantiate them as. Or rather, they are references to these instances. So Ingredient is a class. Paprika is an object that is an instance of an Ingredient. Objects can be thought of as programmatic copy-and-pastes of your class definitions (with exceptions), that you then use within the context of your running program. These copy-and-pastes are independent of each other (so changing your paprika instance's name to "awesome sauce" won't change your horseradish instance's name as well). Hope I didn't confuse. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 16, 2009, 07:33:21 AM One other thing I've noticed you do Yeg, is use "object" and "class" sorta interchangeably. This can lead to all sorts of confusion. To simplify (to the point of being not 100% accurate, but useful for a new/medium developer), think of a class as a data and behavior definition. Classes are templates of a sort. Objects are concrete instances of the classes you instantiate them as. Or rather, they are references to these instances. So Ingredient is a class. Paprika is an object that is an instance of an Ingredient. Ah, yes, good catch. I will have to watch that. Many times I use the word "instance" to mean that, but I can see how I am confusing classes and objects somewhat. I suppose the right thing is to only use the words Class and Object so I and others do not get confused. Also your post was helpful and not at all confusing. I think it's sort of funny that this thread is turning into a miniblog of me learning C#, and now I somehow find myself this morning reading Extreme Programming Adventures in C#, which is apparently a running account of Ron Jeffries learning C# and attempting to apply XP principles to it. I think I will know by the end of chapter one if XP is more my thing, but right now it sounds awesome. Maybe too awesome. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 16, 2009, 09:23:04 AM Is that MS Fortran? Jesus help me. :oh_i_see: Edit to say that if I decide to pick up any other language that has tenuous relations to my work, it will likely be Haskell. More like MS OCaml. I'm a fan of the implicit strong typing, but it's not going to kick Ruby off the perch of the closure-philes. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 16, 2009, 09:50:39 AM I made some code. Have a question, not sure I can do what I am thinking I want to do. Pertinent lines are red.
Card class, obviously: class Card {// private member variables private int fire; private int water; private int air; private int dirt; private string name; // constructor public Card(string n) { name = n; fire = 1; water = 2; air = 3; dirt = 4; } Pertinent bit of CardPile, the general form of any set of cards: private Dictionary<string, Card> containedCards; public CardPile(params string[] cardNames) { containedCards = new Dictionary<string, Card>(); foreach (string s in cardNames) { containedCards.Add(s, Card(s)); } } If it is not obvious, I want to initialize a CardPile with a set of Cards which I pass to a foreach as a string array. I am not really sure I want to do this but it would be nice to see it work. I would be using the Add() method to put cards into the dictionary with the intent of having a card's name be the key (string) and the Card object that gets instantiated based on what string (card name) I give it (real code for that comes later). Nevermind the stubbiness of Card, I'm just worrying about the Add() part and whether it can do what I want. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 16, 2009, 10:07:51 AM 1) That looks good enough. Say, what are the fire/water/air/dirt vars? I get the feeling you're trying to do something that can be done a better way. Manage suite?
2) Typically it'd be better to use "params Card[] cards" as argument to the constructor, because then you don't have to rewrite the constructor (possibly jerking upp ALOT of references elsewhere in the code) if you decide to change the identifier of Card (right now, a string, next, maybe a string and a suite?) When passed around, you'll want to use Card rather than the string key, so most of the time you'll be adding and removing Card instances from the collection. 3) You don't need to declare variables in classes as private. They're private by implication. So "private int x" is identical to "int x" when declared as a class member. Same goes for all class members (methods, enums, nested classes... etc.) Edit: 4) I forgot to mention. Dictionary is a key-unique collection, it can't store multiple objects with the same key. For a normal deck of cards this won't be a problem, but for something more akin Magic, it would be. Are the cards going to be unique, or will there be duplicates? Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 16, 2009, 10:09:15 AM Caveat: I'm coming from Java-land, so I'll probably say something dumb and/or inaccurate about C# syntax.
Minor issues: you're missing the 'new' operator in your Add call Larger issues: A dictionary allows a single value per key. Java behavior is to replace it if you try to add a new value with the same key, but I think C# actually tosses an exception. In your case, this'd mean that you could only have one instance of each card name, otherwise your dictionary would bitch. You probably want a List instead of a Dictionary. Also, this isn't part of your particular question, but if your fire/water/air/dirt values are supposed to be enum-type attributes of the card, standard practice is to make them static, initialize the values in the declaration, and capitalize the names. But C# actually natively supports enums these days, and that's an ever better way to go: http://www.csharp-station.com/Tutorials/Lesson17.aspx Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 16, 2009, 10:29:38 AM Java behavior is to replace it if you try to add a new value with the same key, but I think C# actually tosses an exception. Depends on how you set it. This would throw an exception;dicCards.Add("a", myCard); while this will overwrite the value; dicCards["a"] = myCard; Basically, you're almost always better off using the indexer ([]) to set or add values, because it does all the key-checking for you. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 16, 2009, 11:20:44 AM I am going to have to do more thinking on what goes where.
1) That looks good enough. Say, what are the fire/water/air/dirt vars? I get the feeling you're trying to do something that can be done a better way. Manage suite? Mmm, well I was hoping to ignore this bit until later. Then again, I failed to consider that I would have multiple identical card objects when I picked a Dictionary to hold them, so I might as well scratch it now and use a List. Actually I sketched out this code with an Array and decided I didn't want to try to deal with "holes" in the array. For example, removing a Card from a CardPile would possibly mean that containedCards[23] would be empty/null/whatever. I won't be able to dodge writing a real ShowAllCards() with a sparse array. I will need to think about this. As for how I plan to implement Card itself, the end goal is to have a Card that can have any number of arbitrary properties. There are likely to be several standard ones but I don't want to limit myself to a set of 52 cards with a couple of properties, for example. I'm thinking of Magic here in that I might have a card called Butt with properties like string Title = "Butt", int Fire = 3; string FreeText = "This card has a picture of a butt with a butt-shaped tattoo on it."; string Arbitrary1="Odiferous"; int Pimples=9; and so on. Simply put, I'd like to give someone free reign to make cards as if this were a paper game. At the moment the actual properties are not important to me. 2) Typically it'd be better to use "params Card[] cards" as argument to the constructor, because then you don't have to rewrite the constructor (possibly jerking upp ALOT of references elsewhere in the code) if you decide to change the identifier of Card (right now, a string, next, maybe a string and a suite?) You seem to be saying I should use classes for both key and value (if I use a Dictionary). Is that correct? New idea: on startup I instantiate a CardPile named deck that initializes with all of the cards it contains. After that I can make new CardPiles of various types that simply get a particular object from the master deck object. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 16, 2009, 11:44:31 AM Rewrite.
class CardPile { List<Card> containedCards = new List<Card>; public CardPile(params Card[] cards) { foreach (Card c in cards) { containedCards.Add(c); } } // show number of cards in pile public int GetNumCards() { return containedCards.Count; } } } I think I'm done for today. Next time I will work on setting up the CardPile derivatives such as Hand and Deck. Title: Re: Yegolev Learns C# - was Your app's data Post by: Azaroth on January 17, 2009, 09:22:58 AM So, how many of you guys can I exploit into working for free?
Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on January 17, 2009, 05:25:45 PM I strongly suggest you develop a notation to distinguish between instance variables and local variables/parameters.
The one I use is underscore. Title: Re: Yegolev Learns C# - was Your app's data Post by: MahrinSkel on January 17, 2009, 08:31:32 PM Mine was upper-case vs. lower-case for the first letter of the name. But I learned Pascal first rather than C (the same guy developed Delphi for Borland before he went to MS and built C#).
--Dave Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 19, 2009, 06:36:00 AM I strongly suggest you develop a notation to distinguish between instance variables and local variables/parameters. The one I use is underscore. I disagree. If it is possibly confusing, I usually just tack on "this.", or name the parameter something to distinguish it. Most IDEs have ways for you to distinguish between them anyway. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 19, 2009, 10:43:53 AM Yeah, with modern IDEs it's easy enough to trace back where your variables are declared, but I still use the Hungarian m (without an underscore) for class members out of habit.
Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 19, 2009, 01:19:37 PM Yeah, with modern IDEs it's easy enough to trace back where your variables are declared, but I still use the Hungarian m (without an underscore) for class members out of habit. Yeah, habits die hard :) I mean, it doesn't hurt to do it, but it's unnecessary these days. Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on January 19, 2009, 11:11:14 PM If you are working on code that other people are going to have to interact with or that you yourself might put down and come back to down the road it's well worth it.
Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 20, 2009, 06:26:52 AM I am already a tad confused by C# using both camel and pascal notation, so I will either use _ or this. as a prefix. I think I'd rather use this. for the proper separation in my mind of what language I am writing. I suppose it is as simple as using Class this.object = new Class; inside any methods where I would be using a local?
Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 20, 2009, 08:10:55 AM If you are working on code that other people are going to have to interact with or that you yourself might put down and come back to down the road it's well worth it. That's what meaningful naming is for. I only get annoyed (or simply mentally ignore) any 'm's or '_'s I see. If I am confused, I can use the IDE to find out where this variable is being defined. Generally if I'm confused by someone's code, it's because it's confusing in general. Keeping track of member vars is the least of my problems. Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 20, 2009, 08:18:49 AM I am already a tad confused by C# using both camel and pascal notation, so I will either use _ or this. as a prefix. I think I'd rather use this. for the proper separation in my mind of what language I am writing. I suppose it is as simple as using Class this.object = new Class; inside any methods where I would be using a local? For locals, you DON'T use "this.". For member vars/fields/properties/variables-defined-as-class-variables-outside-of-a-method you would (or use m or _, or whatever you want). Example (in Java, but similar to C# as I understand): public class BokFoo { private String foo; // Member variable public BokFoo(final String foo) { this.foo = foo; // Assigns parameter to class field } public void kungFoo(final String bar) { String foo; // local variable definition masks class field of the same name foo = bar; } public void noFooForU(final String foo) { String foo; // Will cause compilation error, and any decent IDE will highlight this as an error when you type it } } Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 20, 2009, 09:25:56 AM "this" is the keyword to refer to the current instance of the class from inside the class, not the actual scope (so you can't access a static method with this.MyStaticMethod(stuff), because it's not an instanced member.)
Scope cascades in some OO languages so naming variables the same thing on several scope levels will hide the outer variable. This is not the case in C# however, it will simply generate a compiler error if you try to hide outer variables (or anything really.) This is done for clarity. You can't have a class member named "banana" and a method argument called the same (because that argument will exist in the method scope, as a local variable.) The names need to be unique all the way up to and in the class scope. The only exception in C# is hiding of inherited members using new, but that's sorta outside the scope of this post. Get it? :rimshot: Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 20, 2009, 12:53:49 PM What I got was:
1. Use descriptive names. 2. Let the IDE tell you when you fuck up. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 20, 2009, 01:34:21 PM ...sorrie 'bout dat. :heartbreak:
Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 20, 2009, 01:56:28 PM What I got was: 1. Use descriptive names. 2. Let the IDE tell you when you fuck up. Well, I would say, let the IDE try and help as a last resort. Using descriptive names, or even prefixing properties/fields with a special character are much preferred than letting someone else's code (the IDE) figure it out for you :) Sometimes even the IDEs screw up. Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on January 20, 2009, 08:53:16 PM What I got was: 1. Use descriptive names. 2. Let the IDE tell you when you fuck up. Develop good habits. What happens when you starting programming in a scripting language that doesn't have an IDE beyond a basic text editor? So much for right clicking "go to declaration." The problem with using "this" is that you don't have to, you can have a parameter named one thing and an instance variable named the same thing. You can use "this" to differentiate but if you leave off the "this" it will still work. If you name your member variable with an underscore you cannot accidentally use a local variable or parameter of the same name. Plus typing "this" every time is a pain in the ass. If you name your member variables in a special way it makes your code easier to understand and also prevents subtle variable-hiding errors. I prefer underscore to 'm' because it sorts better and reads easier. Title: Re: Yegolev Learns C# - was Your app's data Post by: MahrinSkel on January 21, 2009, 02:42:28 AM Whereas I like the upper/lower trick because it sorts them in together and lets me get straight to the head-slapping moment as I discover yet another scoping error.
--Dave Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 21, 2009, 07:52:07 AM What I got was: 1. Use descriptive names. 2. Let the IDE tell you when you fuck up. Develop good habits. What happens when you starting programming in a scripting language that doesn't have an IDE beyond a basic text editor? So much for right clicking "go to declaration." Then your usage of meaningful names (as opposed to arbitrary names) will help you out. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 21, 2009, 08:12:18 AM What happens when you starting programming in a scripting language that doesn't have an IDE beyond a basic text editor? So much for right clicking "go to declaration." I am approaching this from the opposite direction, actually. All of the non-C# code I write (and have written for 10 years) is done in Vim via PuTTY. If I want to find a declaration, I /varname=<ENTER> and BOOM. For me, descriptive names are a non-issue and I am steeped in all-lower-case var names. The only special convention I use for names is that I actually use all caps for perl filehandles, otherwise it's a free-for-all. I am partial to one- and two-letter var names in many cases and I have code samples that would make a real (non-perl) programmer cry. Although I am getting better since I would otherwise spend way too much time deciphering old code. Changing a var name from $qn to $qname, for example. I also never remember that the IDE can show me shit because I'm used to vi/Vim, so it's really a distant and bonus second after "descriptive names" since my standards are so low. In this case, "descriptive names" includes using a convention that makes things obvious, whether it's _ or m or whatever. I'm very :why_so_serious: about this, but I'd still like to learn the right way to do things. Something being hard to type isn't on my list of concerns. From my perspective, everything in C# is a pain in the ass to type, so "this." isn't even a noticeable bump to me. My problem is actually that I don't get the scoping rules (these will come as time passes). That said, I am interested in learning good habits... but which ones? :awesome_for_real: ...sorrie 'bout dat. :heartbreak: I don't know what you are sorry about, unless I missed the point entirely. Don't forget that this is just a fun thing to do, so let's all be cheerful. Also, keep pounding things into my head until I get them, sometimes that's what it takes. Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 21, 2009, 09:26:39 AM Maybe I digressed a bit, that's what I meant. :awesome_for_real:
Personally I think keeping variables short and concise is more important that being descriptive. Properties, yes, methods, yes, arguments, to a point, but your run-of-the-mill locals? Not really. Why bother? Having type in the variable name is to me a decent waste of succinctness (because with str, int, obj etc. as prefixes, you need to know what type it is to find it in the auto-completer, which is actually the direct opposite of what you're trying to do.) Here's a short example of my notation: Code: private AggregateCache _aggregate(IEnumerable<string> files) { Underscore for class privates (including methods), then very condensed local names. Point being, if you have so many locals that a very short name isn't enough to make them distinct, you're probably doing something wrong. Refactor the method, split it into smaller calls that that are easier to maintain and easier to code in the first place. If you can't fit a method on a full screen height, it's time to slow down and think about what you're trying to do. P.S. edit: I used to use longer variable names, but recent years I've used as short names as possible. Going back to older code, I've found it's equally difficult to decipher a more descriptive name "KeyBytes" and a shorthand like "kb" because you have still have to figure out what "KeyBytes" means in this context, not just what it contains. You will never get to the point where you can look at a variable and say "this is what it stores and how what's stored in it will be used." And let's not get into variable names that are just plain misleading because you couldn't come up with a name that aptly described the data. If "KeyBytes" is storing character-encoded binary data (like "0xf5e403") rather than an array of bytes, you've already added confusion. Then maybe KeyBytesString... So go perl'er! :drillf: Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 21, 2009, 09:41:01 AM What happens when you starting programming in a scripting language that doesn't have an IDE beyond a basic text editor? So much for right clicking "go to declaration." I am approaching this from the opposite direction, actually. All of the non-C# code I write (and have written for 10 years) is done in Vim via PuTTY. If I want to find a declaration, I /varname=<ENTER> and BOOM. For me, descriptive names are a non-issue and I am steeped in all-lower-case var names. The only special convention I use for names is that I actually use all caps for perl filehandles, otherwise it's a free-for-all. I am partial to one- and two-letter var names in many cases and I have code samples that would make a real (non-perl) programmer cry. Although I am getting better since I would otherwise spend way too much time deciphering old code. Changing a var name from $qn to $qname, for example. I also never remember that the IDE can show me shit because I'm used to vi/Vim, so it's really a distant and bonus second after "descriptive names" since my standards are so low. In this case, "descriptive names" includes using a convention that makes things obvious, whether it's _ or m or whatever. I'm very :why_so_serious: about this, but I'd still like to learn the right way to do things. Something being hard to type isn't on my list of concerns. From my perspective, everything in C# is a pain in the ass to type, so "this." isn't even a noticeable bump to me. My problem is actually that I don't get the scoping rules (these will come as time passes). That said, I am interested in learning good habits... but which ones? :awesome_for_real: ...sorrie 'bout dat. :heartbreak: I don't know what you are sorry about, unless I missed the point entirely. Don't forget that this is just a fun thing to do, so let's all be cheerful. Also, keep pounding things into my head until I get them, sometimes that's what it takes. When using a nice IDE (when available) don't forget that most have auto-complete, so longer descriptive names aren't any slower to type (in eclipse, the shortcut is ctrl-space to bring up a list of potential matches. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 21, 2009, 11:14:23 AM There are few arguments that will cause more slapfights among developers than arbitrary coding conventions. Avoid at all costs. Try out a few different things and adopt the practices of other peoples' code that you find readable. Otherwise, keep your head down and for the love of all things holy don't bring up tab or newline conventions.
Title: Re: Yegolev Learns C# - was Your app's data Post by: Bokonon on January 21, 2009, 12:37:02 PM There are few arguments that will cause more slapfights among developers than arbitrary coding conventions. Avoid at all costs. Try out a few different things and adopt the practices of other peoples' code that you find readable. Otherwise, keep your head down and for the love of all things holy don't bring up tab or newline conventions. Braces on their own lines 4EVAR! ;) Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 21, 2009, 03:58:24 PM There are few arguments that will cause more slapfights among developers than arbitrary coding conventions. Avoid at all costs. Try out a few different things and adopt the practices of other peoples' code that you find readable. Otherwise, keep your head down and for the love of all things holy don't bring up tab or newline conventions. John McCarthy has something to tell you. (http://www.buayacorp.com/wp-content/uploads/2007/10/john-mccarthy-poster1.jpg) Title: Re: Yegolev Learns C# - was Your app's data Post by: MahrinSkel on January 21, 2009, 04:05:27 PM Yeah, having learned Pascal first and being prone to longer names (multiple words with in-stream capitalization), I drive Perl and C programmers insane, who apparently are trying to use as few keystroke as possible and/or make their code incomprehensible (Perl programmers compete to create the shortest possible programs, C programmers the most impossible to comprehend). By normal convention, the only short var names in Pascal are counters and token holders (i,j,k, x,y,z, foo, bar) that will never be used outside a single block of code.
And even then you can get into slap-fights over prefixes for objects. For example, I prefix all of my GUI elements with a type identifier (lbl, ed, scrl, cmb, and so on), which annoys some because if you change the type, either you chase down all the references (which is kind of the point, it makes you check to be sure all your code can handle the change) or you have them mislabeled. And there are constant campaigns to enforce common standards inside a shop for clarity, and passive-aggressive resistance to those campaigns. --Dave Title: Re: Yegolev Learns C# - was Your app's data Post by: Tarami on January 21, 2009, 04:20:59 PM C has pre-processor macros. It's basically asking you to write an incomprehesible mess. :awesome_for_real:
Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 22, 2009, 08:30:23 AM Braces on their own lines 4EVAR! Oh, I laughed out loud there. Well played. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 22, 2009, 08:41:52 AM Since we are well-derailed, and waiting for me to write more C# anyway, I will show everyone why Perl and I are going to be married one day.
Code: for $i (reverse 0..$#su){ The @DB array is a debug array. There was a time when I would not have bothered to include such a modern innovation, since it takes up SO MUCH ROOM. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on January 22, 2009, 10:32:22 AM Code: ... The 2nd one of those is redundant, since $nd == $fd was already checked and couldn't change in the interim. NEXT CODE REVIEW!!! Perl is garbage. It's unmaintainable in exchange for requiring 30% fewer keystrokes. That's not a good exchange. At all. Title: Re: Yegolev Learns C# - was Your app's data Post by: Murgos on January 22, 2009, 10:57:49 AM A fellow cubicle dweller once referred to Perl as a Write Only Language.
The man was a complete waste of space, but on that one point he was 100% correct. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on January 26, 2009, 10:25:21 AM Perl is garbage. It's unmaintainable in exchange for requiring 30% fewer keystrokes. That's not a good exchange. At all. It is if you don't maintain anything. I say that with full awareness of what happens when you have to change something, but my job is not to write programs. What we do around here is get The Enterprise functional and move on to the next problem, which is pretty much what Perl was made for. It's just to make shit work and fast, and that is what I love about it, but of course people use it for lots of things today. I'm purposely trying very hard to not bring over any of my crap procedures into C#, and even what seemingly-little progress I have made just within this thread's arc has caused some fascinating changes in how I write shit in day-to-day application. It also helps that lately I have been re-writing a lot of code as new ideas/problems come in and the limits of my current crop of programs are glaring to me. As for the redundancy, that's because I had originally written a long boolean that failed in mysterious ways. This is just a quick redo in if contructs. Since it actually works, I haven't bothered to clean it up. I'd like to not do such things in C#. Title: Re: Yegolev Learns C# - was Your app's data Post by: Salamok on January 26, 2009, 12:28:49 PM For example, I prefix all of my GUI elements with a type identifier (lbl, ed, scrl, cmb, and so on), which annoys some because if you change the type, either you chase down all the references (which is kind of the point, it makes you check to be sure all your code can handle the change) or you have them mislabeled. This seemed to be strongly encouraged by visual studio when i was tinkering with it and made sense to me at the time. After playing in php I have discovered the joys of storing all my form(GUI) elements in a single associative array and using the exact same names as my table fields. Title: Re: Yegolev Learns C# - was Your app's data Post by: naum on January 26, 2009, 09:38:59 PM A fellow cubicle dweller once referred to Perl as a Write Only Language. The man was a complete waste of space, but on that one point he was 100% correct. As somebody who was paid professionally to write and maintain Perl code, that is not necessarily true, although often, people muddling with Perl (which frequently are those not firmly grounded in algorithms and good coding practices anyway) cobble up some hideous, dreadful looking code… That being said, with Ruby (and gem(s)), there is real no reason for Perl as Ruby has all that Perl has, plus a simple, clean OO model (I WILL NEVER EVER HAVE TO BLESS A REFERENT EVER AGAIN!), much more elegant block passing (the coroutine/yield and functional programming although it's not even Lisp-ish as Javascript) and now a kick-ass web development platform (Rails for the full stack, or Sinatra/Ramaze for drop dead simple web applications along with Rack/Phusion Passenger, gives you all that PHP offers in much more elegant, joyous to write and read, bundle)… …though Ruby creator wishes to eradicate the Perlisms from Ruby (the $1, $_, $:, one liner options, etc.…), there's so much code that uses them, I doubt they ever go away totally, other than "let's try not to use these dirty Perl vestiges…"… Or you can go Ruby on the desktop w/Shoes, or FxRuby, or recently I saw a demo with a local outfit that's selling a desktop time tracking application (JotBot), written in JRuby and totally cross-platform (they also released a Ruby gem called MonkeyBars that aids in the UI code minimizing…). Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on January 26, 2009, 10:23:50 PM Perl is great for text processing. Using it for anything else is a little silly though. I mean I would never use it for a CGI script or something like that these days but if you want to take a text file and spit out a different text file Perl is pretty damn awesome.
As far as being read-only, I took a set of Perl scripts that was made for documenting JavaScript and changed it into one that documented an entirely different scripting language in about 3 hours. The difficult thing about Perl is that if you don't know it well it's essentially impossible to make heads or tails of it. Title: Re: Yegolev Learns C# - was Your app's data Post by: MahrinSkel on January 26, 2009, 10:44:56 PM Possibly the most famous 6 lines of Perl ever:
Code: s''$/=\2048;while(<>){G=29;R=142;if((@a=unqT="C*",_)[20]&48){D=89;_=unqb24,qT,@ Any language that can produce a felony in 6 lines, I don't want to mess with. --Dave PS: And if Schild is feeling paranoid enough that the MPAA may still be pursuing DeCSS distribution, I'd like to let you know I've got a friend that might start posting from my house, by the name screen name of Gurghe Dumat (since I just committed said felony, and he's aiding and abetting if he doesn't take it down and ban me) Title: Re: Yegolev Learns C# - was Your app's data Post by: schild on January 26, 2009, 11:10:18 PM I said What?
DeCSS has been on t-shirts for years. I think we're way past people trying to procsecute for DeCSS distribution. Title: Re: Yegolev Learns C# - was Your app's data Post by: Logik on January 28, 2009, 08:11:12 PM A fellow cubicle dweller once referred to Perl as a Write Only Language. The man was a complete waste of space, but on that one point he was 100% correct. I've always used the WORN acronym: Write Once, Read Never. Having said that, I echo the sentiments of Margalis. I'm all about using Perl when it comes to text processing, but for everything else my choice is usually Python. Title: Re: Yegolev Learns C# - was Your app's data Post by: Murgos on January 29, 2009, 08:45:41 AM We use a lot of Perl for processing log files and setting up clean sandboxes and environments and such for development.
No one has ever suggested using it for the actual work we get paid for though. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on February 02, 2009, 05:57:36 AM Back to C#, I have a design question. I have a Card class that represents a generic card. When instantiating an object, I want a particular card. What I imagine can happen is that I pass a string argument to Card and it instantiates that particular card; let's call it The Abyss just for an example. The constructor needs to know what attributes to give The Abyss. Would it be fine to have this data stored inside the Card class definition? Seems like a good idea to me but I seem to flop most often on this part of OOP.
Title: Re: Yegolev Learns C# - was Your app's data Post by: Murgos on February 02, 2009, 07:55:53 AM Not that I do a lot of that type of work but offhand my guess is to create a card Interface (a type of abstract base class) that forces a certain set of generic components that you use on the object and then create child classes for each type of card.
A deck then would consist of strongly-typed card objects that are each treated individually but are all of the same 'type' so that you can create generic containers and such to manipulate them with. This will then allow you to use introspection to do neat tricks with manipulating things at run-time. Assuming the point of this exercise is still more toward learning OOP and C#. Someone else will, I am sure, come along and call me a doodie head but the key-words you need to read up on for this are Introspection, Polymorphism and Interface. Title: Re: Yegolev Learns C# - was Your app's data Post by: tazelbain on February 02, 2009, 09:18:41 AM I'd put the card stats in xml document or database rather than clutter your class with the data for all your cards and get Moose an' Squirrel.
Title: Re: Yegolev Learns C# - was Your app's data Post by: schild on February 02, 2009, 09:27:11 AM I'd card stats in xml document or database rather clutter you class with the data for all your cards. What. You should read this sentence aloud. Title: Re: Yegolev Learns C# - was Your app's data Post by: Yegolev on February 02, 2009, 10:02:39 AM Assuming the point of this exercise is still more toward learning OOP and C#. It is. I'll take your direction since I didn't understand much of it. :awesome_for_real: I'd card stats in xml document or database rather clutter you class with the data for all your cards. What. You should read this sentence aloud.If you read it in a Boris Badenoff voice and tack "and get moose and squirrel" on the end, you're fine. Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on February 02, 2009, 10:51:56 AM In Soviet Russia, Card class designs you!
Yegolev, what you want here is a template class. The template class stores the information about a card that persists from game to game. The name, the flavor text, the cost, etc. Then you have a card class that has a reference to its template as well as information that exists for a single game. Whether it's tapped, counters on it, etc. So to create a new card you either have a factory or the template base class will have a newCard() method that creates a new Card with itself as a template and returns it. Something like: Code: public abstract class CardTemplate { Title: Re: Yegolev Learns C# - was Your app's data Post by: sidereal on February 02, 2009, 10:53:04 AM And the next step would be to make it data-driven instead of hardcoded cards. So you'd load your templates up from an xml file or whatever. But for the first few iterations, it's fine to just dump them into classes.
Title: Re: Yegolev Learns C# - was Your app's data Post by: Morat20 on February 02, 2009, 10:53:41 AM Not that I do a lot of that type of work but offhand my guess is to create a card Interface (a type of abstract base class) that forces a certain set of generic components that you use on the object and then create child classes for each type of card. Place them in xml or a database, to echo tazelbain.A deck then would consist of strongly-typed card objects that are each treated individually but are all of the same 'type' so that you can create generic containers and such to manipulate them with. This will then allow you to use introspection to do neat tricks with manipulating things at run-time. Assuming the point of this exercise is still more toward learning OOP and C#. Someone else will, I am sure, come along and call me a doodie head but the key-words you need to read up on for this are Introspection, Polymorphism and Interface. You don't want to have to go dig into your code because some card changed from 2 black mana to 3, or to add new cards. What you send your class is a key for the card's DB entry, and the card will then load various attributes from the DB. Never hard code what you can stick in a DB or xml file (or hell, flat text). Why would you recompile your code whenever you add a new card? Title: Re: Yegolev Learns C# - was Your app's data Post by: Margalis on February 02, 2009, 11:07:54 PM Data driven is the way to go. I try to code things so that the code itself is just the engine that knows how to follow some rules and all the information comes from some place else.
Even if your cards have very special rules it's better to give those rules names and make them data driven so that you can always mix and match if you ever feel like it. |