Related
I just recently found out here that it is possible (at least in c#) to look up private fields and properties due to reflection.
I was surprised, although I knew that somehow constructs like the DataContractSerializer class need the possibility to access them.
The question now is, if anyone can access every field in my classes, this is kind of insecure, isn't it? I mean what if someone has a private bool _isLicensed field. It could be changed easily!
Later I found out here that the field accessors are not meant as a security mechanism.
So how do I make my Application safe, meaning how do I prevent anyone other than me from changing essential status values inside my classes?
The question now is, if anyone can access every field in my classes, this is kind of insecure, isn't it?
Not everyone can. Only code with sufficient permissions - trusted code. Untrusted code is restricted quite a bit. On the other hand, if the person who wants to use reflection has your assembly, they can run trusted code on their own machine. That's not a new attack vector though, as if they've got your code they could also modify it to make the field public in the first place.
Basically, if code is running on their machine, you should expect them to be able to do pretty much anything with it. Don't rely on access modifiers to keep anything secret.
So how do I make my Application safe, meaning how do I prevent anyone other than me from changing essential status values inside my classes?
If the hostile user is running your code themselves, you pretty much can't. You can make it harder for them, but that's an arms race which is no fun.
So one option in some cases is not to let anyone else run your code - host it on the web in an environment you've locked down. That's not appropriate in all cases, of course.
If you have to let users run the code themselves, you need to weigh up the downsides of them tampering with the costs of making that tampering difficult. We can't really help you with that balancing act - we don't have any idea what your application is, or what the costs involved are (reputational, financial etc).
private public and so on are a part of http://en.wikipedia.org/wiki/Encapsulation. the use is to make your API clear and to avoid mistakes.
there is no solid way to avoid people messing with your program.
you may have noticed that all programs are cracked in a few days usually.
in .net it is VERY easy because of IL code been very readable http://ilspy.net/ and such allow you to take any DLL and just read it like C# code.
you can make it more annoying to read your code using obfuscator
http://en.wikipedia.org/wiki/List_of_obfuscators_for_.NET
but applications like http://de4dot.com/
break this VERY easily.
SecureString is a nice trick: https://msdn.microsoft.com/en-us/library/system.security.securestring%28v=vs.110%29.aspx
writing your code in low level language like c++ might make cracking your code really annoying. but soon a skilled hacker will do whatever he wants with your program.
the only option that might be safe is providing your application as a cloud service where the user only sees the screen output and sends keyboard/mouse input.
This was meant to be a comment for John Skeets answer but ran out of room..
Great answer by the way, but I also must add that code is not meant to be secure its meant to clearly defined.
Most developers know how to change classes and inject into classes. There are many utilities to not only decompile your code but to also allow injection into it.
I wouldn't spend to much effort trying to your make code more secure, I would try and expect the code to be changed. Many programming languages do not have such modifiers as private, public, internal, protected etc. They rely on the developers to understand the consequences of using this code on their own. These programming languages have been quite successful as the developers understand that modifying, calling or injecting into code the API does not specify has results that the developing company cant and will not support.
Therefore, expect your code to be modified and ensure your applications responds to invalid changes appropriately.
Sorry if this seems like a comment...
To add to all the other answers, a simple way of looking at it is this: If the user really wants to break your code, let them. You don't have to support that usage.
Just don't use access modifiers for security. Everything else is user experience.
I bet this is the dumbest question anyone could ever ask but I can't seem to wrap my head around this topic. I understand a Class is used to create an object (when you instantiate it) but what is confusing me is, "When do you know if it is viable to create a Class in a program?".
Say hypothetically your creating a program that gathers an input from the user (be it a name, a number or any other details), use those details to do some calculations and then storing all of it locally. Its basic but would you need to use a class to make the program smoother/faster or more maintainable?
Am I confusing myself?
EDIT: I am mostly using programs like Visual Studio and NetBeans IDE.
As you say a class is a construct that allows you to describe a type, which has properties methods and events on it.
In simple situations you can easily get away with not using classes, but in larger, more complex projects having a properly thought out object model makes things so much easier. Easier to maintain, extend, reuse, read.
It may feel like more work at the time (and in most cases it probably is), but it is definitely not wasted effort if you're creating something that will need to be supported.
In Java you don't have a choice: you can't write code that's outside of a class.
A class is a template for instances. It encapsulates state and behavior together into a single software component. You write programs by creating instances of classes that interact together to accomplish your goals.
would you need to use a class to make the program smoother/faster or
more maintainable?
I can't speak for C#, but in Java you don't have a choice. You either create a single class that does all that in a main class or you break it up into several classes that handle different parts of the problem (e.g. I/O, calculations, persistence, etc.) You have to have one or more classes.
You write classes and create objects from them because they map well to the kind of problems that you want to solve. They're either real objects that model physical things in the world (e.g. Person, Car, Bank, etc.) or reifications of ideas (e.g. PersonFactory, Account, etc.) You choose to write object-oriented code because objects model the problem you'd like to solve well.
Some problems lend themselves to functional programming. There are more than one way to write programs to solve problems.
here is a very simple / easy to understand Tutorial that will help you in learning / understanding C# especially Classes also look at Structs as well
C# Class Tutorial
Classes are primarily for us humans to help us organize code. They don't necessarily make software run faster on a computer. When I first learned about classes, I found it helpful to model "real-world" objects. For example, if I wanted to write a program that calculates the area and perimeter of geometrical shapes, I would create a simple Shape class which defines the abstract methods which do the calculations. Then I extend this class to create different kinds of shapes, say Circle, Square, and Triangle. By starting with simple applications of classes and through more programming experience, I have been able to gradually see other places to use classes.
For a simple program, like your example, to gather input from the user (name, number, comment), you can easily do this without creating a new class, excluding the fact that languages like C# and Java require a static class to put your main function inside. If you used a non-object oriented language like C, you could easily do it without a class.
For a simple class like you describe, in fact, it might seem like a little extra work to have to create the class (although it is probably negligible). However, that is only because you are referring to a small set of data.
The point where it gets useful to create classes is when you have many pieces of data. The class acts as a way to group that data together, and possibly store it locally as one. You can also add other related methods onto each class that are directly related to the class members.
When you app becomes more complex, say you need to keep track of customers, products, billing information (Credit Card, PayPal, ...), addresses (ship to, and billing), is when classes become extremely valuable in keeping each bit of information together as well as relating each of those larger "bundles" (classes) of information to each other.
You could have a customer who has an order who has a bill to and a shipping address. Each of these classes, itself has many fields inside of it. But you can relate the larger concept of customer to a target shipping address a lot easier with classes.
As far as "When do you know if it is viable to create a Class in a program?", the answer is not always easy, but any time you see data fields which naturally fit together (well, like an address, or a product description or billing information). I wish I had a more concrete answer, but it really depends on what you are building, and what type of data you are working with.
And no, it's not the dumbest question anyone could ever ask! I hope this helps your understanding.
1. When do you know if it is viable to create a Class in a program?
I think just about everyone who answers this question will have a slightly different response, but typically, you should use Classes to modularize your program's functionality. In your example, you said that your program might take input from the user, use input to perform some calculations, and persist the user data and calculation results somewhere. You could have the following three classes:
UserInput - handles keyboard input and converting it into some easier-to-process format
Calculator - processes all input after it has been converted
DataPersistence - handles reading/writing from/to disk, or database, or whatever you need.
This way, all of your code isn't just piled up inside a massive Java/C# main() call, you can focus on the smaller parts independently of each other. The interaction between these components is what determines your program's behavior.
2. Its basic but would you need to use a class to make the program smoother/faster or more maintainable?
Classes may actually end up adding overhead to your program because of how objects are referenced in languages like Java or C#, but they make your code much easier to read and modify than if your program was written inside one gigantic function. This is sort of analogous to dividing up math textbooks into chapters. If Algebra, Calculus, and Differential Equations were all condensed into a single chapter of the text book, then the text book wouldn't be very useful to those who want to skip ahead to the Calculus part. The overhead of adding chapter headings is negligible because it allows the author/reader to focus on certain portions of the book.
Likewise, Classes help you divide up your work so it's easier to maintain later. Speed/Performance are generally not affected by how you divide your program into classes, provided you do it intelligently - this is where the real artistry of Object Oriented Design manifests itself :)
There is this famous quote that says
Procedural code gets information then
makes decisions. Object-oriented code
tells objects to do things. — Alec
Sharp
The subject of the post is precisely about that.
Let's assume we are developing a game in which we have a Game where there is a Board.
When facing the problem of deciding which methods are we going to implement on the Board class, I always think of two different ways:
The first approach is to
populate the Board class with getSize(), getPieceAt(x, y), setPieceAt(x, y, piece). This will seem reasonable and is what is generally found in libraries/frameworks. The Board class has a set of internal features that wants to share and has a set of methods that will allow the client of the class to control the class as he wishes. The client is supposed to ask for the things he needs and to decide what to do. If he wants to set all board pieces to black, he will "manually" iterate over them to accomplish that goal.
The second approach is about
looking for Board's dependent classes, and see what they are "telling" it to do. ClassA wants to count how many pieces are red, so I'd implement a calculateNumberOfRedPieces(). ClassB intends to clear all the pieces on the Board(set all of them to NullPiece, for example), so I'd add a clearBoard() method to the Board class. This approach is less general, but allows for a lot more flexibility on other aspects. If I "hide" Board behind an IBoard interface, and decide that I'd want to have a board with infinite size, doing in the first way, I'd be stuck, as I'd have to iterate over an infinite number of items! On the other hand, in this way, I could do fine (I could, for instance, assume all pieces are null other than the ones contained in a hashtable!).
So...
I am aware that if I intend to make a library, I am probably stuck with the first approach, as it is way more general. On the other hand, I'd like to know which approach to follow when I am in total control of the system that'll make use of the Board class -- when I am the one who is going to also design all the classes that'll make use of the Board. Currently, and in the future (won't the second approach raise problems if later I decide to add new classes that are dependent on the Board with different "desires"?).
The quote is really warning you away from data structures that don't do anything with the data they hold. So your Board class in the first approach might be able to be done away with and replaced by a generic collection.
Regardless, the Single Responsibility Principle still applies, so you need to treat the second approach with caution.
What I would do is invoke YAGNI (you aren't gonna need it) and try to see how far I could go using a generic collection rather than a Board class. If you find that later you do need the Board class its responsibility will likely be much more clear by then.
Let me offer the contrarian point of view. I think the second approach has legs. I agree with the single responsibility principle, but it seems to me that there's a defensible single mission/concern for a Board class: Maintaining the playing field.
I can imagine a very reasonable set of methods such as getSize(), getPiece(x,y), setPiece(x, y, color), removePiece(x, y), movePiece(x1,y1,x2,y2), clear(), countPieces(color), listPiecePositions(color), read(filename), write(filename), etc. that have a congent and clear shared mission. The handling of those board-management concerns in an abstracted way would allow other classes to implement game logic more cleanly, and for either Board or Game to be more readily extended in the future.
YAGNI is all well and good, but my understanding is that it urges you to not start building beautiful edifices with the hope that one day they'll be usefully occupied. For example, I wouldn't spend any time working toward the future possibility of an infinite playing surface, a 3D playing surface, or a playing surface that can be embedded onto a sphere. If I wanted to take YAGNI very seriously, I wouldn't write even straightforward Board methods until they were needed.
But that doesn't mean I would discard Board as a conceptual organization or possible class. And it certainly doesn't mean that I wouldn't put any thought at all into how to separate concerns in my program. At least YAGNI in my world doesn't require you start with the lowest-level data structures, little or nothing by way of encapsulation, and a completely procedural approach.
I disagree with the notion that the first approach is more general (in any useful way), or what appears to the the consensus that one should "just see how far you can get without abstracting anything." Honestly, that sounds like how we solved eight queens. In 1983. In Pascal.
YAGNI is a great guiding principle that helps avoid a lot of second system effect and similar bottoms-up, we-can-do-it-so-we-should mistakes. But YAGNI that's crossed the Agile Practice Stupidity Threshold is not a virtue.
CurtainDog is right, invoke Yagni and figure out what you actually need right now, implement that, then make sure it's not going to prevent any features that may be desirable in the future.
The second approach violates the principle that superclasses should not know about each of its subclasses. I think the element you're missing is that the base class can define template methods, like getBoardSize, countRedPieces, countBlackPieces, that can be overridden by subclasses and your superclass has code that uses those template methods, therefore telling its subclasses what to do, but not how to do it.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
How should you decide when it comes to choosing the first class to be written in a new application in the world of OOP&D?
If your application seems to have many independent & dependent modules, is there a useful guideline to keep in mind before having a decision?
Although there might not be any concrete rules or a formulation, I am sure there are some basic issues to think about first.
Thanks!
Consider using TDD practice.
Write your test first, formulating it like:
[TestClass]
public class WhenUserDoesThingX()
{
[TestMethod]
public void MessageYIsDisplayed()
{
// ... var actual =
Assert.AreEqual("Y", actual);
}
}
The first class would be whatever you have to implement for the code of WhenUserDoesThingX to compile.
If you're following a very strict OOP paradigm then why not start with the lowest level of your domain? Typically this is something like a "Manager", "Scene", "Main" or "World" which will then 'possess' all your objects.
It depends on the methodology really.
If you're using scrum or something scrum-ish, you might want to start out by writing some basic functionality. Also if you're not a company and you are developing something on your own, it's nice to have something very basic you can show. Usually this means the core of your application. Start by making a tiny bit of working functionality and add the rest later!
However, basic waterfall methodology focuses on rewriting as few code as possible. They first define everything as good as possible and then implement everything part by part or even layer by layer. In that case, you should start with the data layer classes and some unit tests for them.
You can also try test-driven development! In that case, the first thing you write is unit tests for classes that are doing nothing. After that, implement the functionality in the classes and try to keep the unit tests green while doing so!
If you've done much design work up front, then you should have a good idea what each module is supposed to do, so in theory you could jump in wherever you want and code as if everything else is already in place.
I'll tell you that I'm building my first major PHP project right now, without any frameworks, etc. While building it, I have a pretty decent idea of how I want things to fall into place, so what I'm doing is starting with the minimum amount I need to get something to display on the screen. Where the code is expecting a class to exist, I write it as if it already does, and I'll make barebones classes so that it'll run without errors.
For example, I have a method which tries to decide which "Controller" class to hand things off to. It defaults to my "Home_Controller" class and calls new Home_Controller() but the Home_Controller, for the moment, is just this:
class Home_Controller {
}
It might be different in a corporate setting, but since you're starting from scratch I'm assuming you're working on your own project like me. The best thing is to just start with something that will give you immediate visual feedback. The ability to look at something and say "wow, I made that" will keep you going and push you through the more mundane parts of the project.
That's just my two cents.
In my experience this is decided by the road map.
When you are about to start a new project, you must to define some landmark's in the road. This landmark's or sub-functionalities have a sub-domain of all your objects associated with it, so it's define a place to start.
May be this is not much academical, but it's work for me.
Ok, even I am the one who asked the question, I would like to share my opinion as well; but thanks to all of you for your responses.
My opinion-although I don't want to insist as it is the best practice- is basically something like below.
(#AgentConundrumIt: Yes, you are right, this is my own project.)
The list of things I plan to do before selecting class to write in the first place:
Get the final requirements needed for
the application from the customer
Have a picture in your mind on the
possible the UI
Decide what development
tools, environment, technologies,
tools etc. you need to use
List the modules of the application and see
which modules are dependent or in relation to other
ones directly in terms of domain logic
List the possible "INDEPENDENT" or "Stand-alone" classes you need to create for each module of the application
Design them "on the paper" with the best OOP structure so that they can be testable
Score them with their logical complexity, possible dependencies(if still exist) and probability of possible future changes
Choose a class to start with the lowest score (High score means hard to implement and possible to be changed in the future and harder to unit-test at this stage as well, relatively)
Start writing the TEST(s) that fails for the chosen class
Develop your first class; keep going developing your first class until you pass all your tests for it.
So basically I have something like this in my mind as a start-up.
What do you think?
I am unwilling to invest enough into any single class when starting a project to make the process of choosing a first class even noteworthy, much less require all the steps you have listed.
Usually there is some module (package) that is either a clear choice or at least part of a the key central core. I start there. Relationships are often the most important part of any model, so I start by creating classes and defining only the relationships, mapping them and writing unit tests for the mappings, and looking for any missing relationships or awkwardness.
Finding low dependency classes that are likely to be essential to the project is often a place I want to start some lower level developer, not myself. I myself want to jump into the key parts of a design where there are the most dependencies and find any problems ASAP. I tend to move a set of classes forward as a meaningful iteration or two before I start working in earnest on any one. Usually by the time that process is done I know exactly where I want to "start".
The project I'm working on has just hit 4200 lines in the main C# file, which is causing IntelliSense to take a few seconds (sometimes up to 6 or so) to respond, during which Visual Studio locks up. I'm wondering how everyone else splits their files and whether there's a consensus.
I tried to look for some guides and found Google's C++ guide, but I couldn't see anything about semantics such as function sizes and file sizes; maybe it's there - I haven't looked at it for a while.
So how do you split your files? Do you group your methods by the functions they serve? By types (event handlers, private/public)? And at what size limit do you split functions?
To clarify, the application in question handles data - so its interface is a big-ass grid, and everything revolves around the grid. It has a few dialogs forms for management, but it's all about the data. The reason why it's so big is that there is a lot of error checking, event handling, and also the grid set up as master-detail with three more grids for each row (but these load on master row expanded). I hope this helps to clarify what I'm on about.
I think your problem is summed up with the term you use: "Main C# file".
Unless you mean main (as in the method main()) there is no place for that concept.
If you have a catch-all utility class or other common methods you should break them into similar functional parts.
Typically my files are just one-to-one mappings of classes.
Sometimes classes that are very related are in the same file.
If your file is too large it is an indication your class is too big and too general.
I try to keep my methods to half a screen or less. (When it is code I write from scratch it is usually 12 lines or fewer, but lately I have been working in existing code from other developers and having to refactor 100 line functions...)
Sometimes it is a screen, but that is getting very large.
EDIT:
To address your size limit question about functions - for me it is less about size (though that is a good indicator of a problem) and more about doing only one thing and keeping each one SIMPLE.
In the classic book "Structured Programming" Dijkstra once wrote a section entitled: "On our inability to do much." His point was simple. Humans aren't very smart. We can't juggle more than a few concepts in our minds at one time.
It is very important to keep your classes and methods small. When a method gets above a dozen lines or so, it should be broken apart. When a class gets above a couple of hundred lines, it should be broken apart. This is the only way to keep code well organized and manageable. I've been programming for nearly 40 years, and with every year that has gone by, I realize just how important the word "small" is when writing software.
As to how you do this, this is a very large topic that has been written about many different times. It's all about dependency management, information hiding, and object-oriented design in general. Here is a reading list.
Clean Code
SOLID
Agile Principles, Patterns, and Pratices in C#
Split your types where it's natural to split them - but watch out for types that are doing too much. At about 500 lines (of Java or C#) I get concerned. At about 1000 lines I start looking hard at whether the type should be split up... but sometimes it just can't/shouldn't be.
As for methods: I don't like it when I can't see the whole method on the screen at a time. Obviously that depends on size of monitor etc, but it's a reasonable rule of thumb. I prefer them to be shorter though. Again, there are exceptions - some logic is really hard to disentangle, particularly if there are lots of local variables which don't naturally want to be encapsulated together.
Sometimes it makes sense for a single type to have a lot of methods - such as System.Linq.Enumerable but partial classes can help in such cases, if you can break the type up into logical groups (in the case of Enumerable, grouping by aggregation / set operations / filtering etc would seem natural). Such cases are rare in my experience though.
Martin Fowler's book Refactoring I think gives you a good starting point for this. It instructs on how to identify "code smells" and how to refactor your code to fix these "smells." The natural result (although it's not the primary goal) is that you end up with smaller more maintainable classes.
EDIT
In light of your edit, I have always insisted that good coding practice for back-end code is the same in the presentation tier. Some very useful patterns to consider for UI refactorings are Command, Strategy, Specification, and State.
In brief, your view should only have code in it to handle events and assign values. All logic should be separated into another class. Once you do this, you'll find that it becomes more obvious where you can refactor. Grids make this a little more difficult because they make it too easy to split your presentation state between the presentation logic and the view, but with some work, you can put in indirection to minimize the pain caused by this.
Don't code procedurally, and you won't end up with 4,200 lines in one file.
In C# it's a good idea to adhere to some SOLID object-oriented design principles. Every class should have one and only one reason to change. The main method should simply launch the starting point for the application (and configure your dependency injection container, if you're using something like StructureMap).
I generally don't have files with more than 200 lines of code, and I prefer them if they're under 100.
There are no hard and fast rules, but there's a general agreement that more, shorter functions are better than a single big function, and more smaller classes are better than 1 big class.
Functions bigger than 40 lines or so should make you consider how you can break it up. Especially look at nested loops, which are confusing and often easy to translate to function calls with nice descriptive names.
I break up classes when I feel like they do more than 1 thing, like mix presentation and logic. A big class is less of a problem than a big method, as long as the class does 1 thing.
The consensus in style guides I've seen is to group methods by access, with constructors and public methods on the top. Anything consistent is great.
You should read up on C# style and refactoring to really understand the issues you're addressing.
Refactoring is an excellent book that has tips for rewriting code so that behavior is preserved but the code is more clear and easier to work with.
Elements of C# Style is a good dead tree C# style guide, and this blog post has a number of links to good online style guides.
Finally, consider using FxCop and StyleCop. These won't help with the questions you asked, but can detect other stylistic issues with your code. Since you've dipped your toe in the water you might as well jump in.
That's a lot, but developing taste, style and clarity is a major difference between good developers and bad ones.
Each class should do one small thing and do it well. Is your class a Form? Then it should not have ANY business logic in it.
Does it represent a single concept, like a user or a state? Then it shouldn't have any drawing, load/save, etc...
Every programmer goes through stages and levels. You're recognizing a problem with your current level and you are ready to approach the next.
From what you said, it sounds like your current level is "Solving a problem", most likely using procedural code, and you need to start to look more at new ways to approach it.
I recommend looking into how to really do OO design. There are many theories that you've probably heard that don't make sense. The reason they don't is that they don't apply to the way you are currently programming.
Lemme find a good post... Look through these to start:
how-do-i-break-my-procedural-coding-habits
are-there-any-rules-for-oop
object-oriented-best-practices-inheritance-v-composition-v-interfaces
There are also posts that will refer you to good OO design books. A "Refactoring" book is probably one of the very best places you could start.
You're at a good point right now, but you wouldn't believe how far you have to go. I hope you're excited about it because some of this stuff in your near future is some of the best programming "Learning Experiences" you'll ever have.
Good luck.
You can look for small things to change and change each slowly over time.
Are all the methods used in that class only? Look for support methods, such as validation, string manipulation, that can be moved out into helper/util classes.
Are you using any #region sections? Logical groupings of related methods in a #region often lend themselves to being split into separate classes.
Is the class a form? Consider using User Controls for form controls or groups of form controls.
Sometimes large classes evolve over time due to lots of developers doing quick fixes / new features without considering the overall design. Revisit some of the design theory links others have provided here and consider on-going support to enforce these such as code reviews and team workshops to review design.
Well, I'm afraid to say that you may have a bigger issue at hand than a slow load time. You're going to hit issues of tightly coupled code and maintainability/readability problems.
There are very good reasons to split class files into smaller files (and equally good reasons to move files to different projects/assemblies).
Think about what the purpose that your class is supposed to achieve. Each file should really only have a single purpose. If it's too generalized in its goal, for example, "Contain Shopping Basket Logic", then you're headed down the wrong path.
Also, as mentioned, the term you use: "Main C# file" just reeks that you have a very procedural mindset. My advise would be to stop, step back, and have a quick read up on some of the following topics:
General OOP principles
Domain-driven design
Unit testing
IoC Containers
Good luck with your searches.
Use Partial classes. You can basically break a single class into multiple files.
Perhaps the OP can respond: is your project using Object-Oriented Programming? The fact that you use the word "file" suggests that it is not.
Until you understand object orientation, there is no hope for improving your code in any important way. You'd do better to not split up the file at all, wait until it grows to be unbearably slow and buggy, then instead of bearing it any more, go learn OO.
The Intellisense parser in Visual Studio 2008 seems to be considerably faster than the one 2005 (I know they specifically did a lot of work in this area), so although you should definitely look into splitting the file up at some point as others have mentioned, Visual Studio 2008 may resolve your immediate performance problem. I've used it to open a 100K+ line Linq to SQL file without much issue.
Split the code so that each class/file/function/etc. does only One Thing™. The Single Responsibility Principle is a good guideline for splitting functionality into classes.
Nowadays, the largest classes that I write are about 200 lines long, and the methods are mostly 1-10 lines long.
If you have regions of code within a class, a simple method is to use the partial keyword and breakout that definition of the class into that file. I typically do this for large classes.
The convention I use is to have the ClassName_RegionName.cs. For example, if I want to break out a class that manages the schema for a database connection, and I called the class DatabaseConnection I would create a file called DatabaseConnection.cs for the main class and then DatabaseConnection_Schema.cs for the schema functionality.
Some classes just have to be large. That's not bad design; they are just implementation-heavy.